Repository: alibaba/fastjson2
Pull request #3226 (2024-12-30 19:38:40)

Bump org.apache.fury:fury-core from 0.8.0 to 0.9.0
Bumps org.apache.fury:fury-core from 0.8.0 to 0.9.0.


[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=org.apache.fury:fury-core&package-manager=maven&previous-version=0.8.0&new-version=0.9.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)


</details>

codecov-commenter: ## [Codecov](https://app.codecov.io/gh/alibaba/fastjson2/pull/3226?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=alibaba) Report
All modified and coverable lines are covered by tests :white_check_mark:
> Project coverage is 72.47%. Comparing base [(`9d3e91c`)](https://app.codecov.io/gh/alibaba/fastjson2/commit/9d3e91cf58a688e6dfe080c1ca9e5dc91b804f1c?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=alibaba) to head [(`86da30f`)](https://app.codecov.io/gh/alibaba/fastjson2/commit/86da30f36c4bc5f38e9b527f749cf1785002d820?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=alibaba).
> Report is 1 commits behind head on main.

<details><summary>Additional details and impacted files</summary>


```diff
@@             Coverage Diff              @@
##               main    #3226      +/-   ##
============================================
- Coverage     74.38%   72.47%   -1.92%     
+ Complexity    23665    22820     -845     
============================================
  Files           668      668              
  Lines         92089    92071      -18     
  Branches      20577    20509      -68     
============================================
- Hits          68498    66726    -1772     
- Misses        15233    16794    +1561     
- Partials       8358     8551     +193     
```

</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/alibaba/fastjson2/pull/3226?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=alibaba).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=alibaba).


Patch:
diff --git a/benchmark/pom.xml b/benchmark/pom.xml
index f75b85771..8557d2c3a 100644
--- a/benchmark/pom.xml
+++ b/benchmark/pom.xml
@@ -124,7 +124,7 @@
         <dependency>
             <groupId>org.apache.fury</groupId>
             <artifactId>fury-core</artifactId>
-            <version>0.8.0</version>
+            <version>0.9.0</version>
         </dependency>
         <dependency>
             <groupId>org.msgpack</groupId>
diff --git a/core/src/main/java/com/alibaba/fastjson2/JSONWriter.java b/core/src/main/java/com/alibaba/fastjson2/JSONWriter.java
index d2aa6e3fe..f2164d013 100644
--- a/core/src/main/java/com/alibaba/fastjson2/JSONWriter.java
+++ b/core/src/main/java/com/alibaba/fastjson2/JSONWriter.java
@@ -30,6 +30,7 @@ public abstract class JSONWriter
         implements Closeable {
     static final long WRITE_ARRAY_NULL_MASK = NullAsDefaultValue.mask | WriteNullListAsEmpty.mask;
     static final char[] DIGITS = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
+    static final byte PRETTY_NON = 0, PRETTY_TAB = 1, PRETTY_2_SPACE = 2, PRETTY_4_SPACE = 4;
 
     public final Context context;
     public final boolean utf8;
@@ -49,8 +50,7 @@ public abstract class JSONWriter
     protected IdentityHashMap<Object, Path> refs;
     protected Path path;
     protected String lastReference;
-    protected boolean pretty;
-    protected int indent;
+    protected byte pretty;
     protected Object attachment;
 
     protected JSONWriter(
@@ -71,7 +71,15 @@ public abstract class JSONWriter
 
         // 64M or 1G
         maxArraySize = (context.features & LargeObject.mask) != 0 ? 1073741824 : 67108864;
-        pretty = (context.features & PrettyFormat.mask) != 0;
+        if ((context.features & PrettyFormatWith4Space.mask) != 0) {
+            pretty = PRETTY_4_SPACE;
+        } else if ((context.features & PrettyFormatWith2Space.mask) != 0) {
+            pretty = PRETTY_2_SPACE;
+        } else if ((context.features & PrettyFormat.mask) != 0) {
+            pretty = PRETTY_TAB;
+        } else {
+            pretty = PRETTY_NON;
+        }
     }
 
     public final Charset getCharset() {
@@ -682,8 +690,8 @@ public abstract class JSONWriter
     }
 
     public static JSONWriter ofPretty(JSONWriter writer) {
-        if (!writer.pretty) {
-            writer.pretty = true;
+        if (writer.pretty == PRETTY_NON) {
+            writer.pretty = PRETTY_TAB;
             writer.context.features |= PrettyFormat.mask;
         }
         return writer;
@@ -1613,6 +1621,11 @@ public abstract class JSONWriter
             return;
         }
 
+        if (map.isEmpty()) {
+            writeRaw('{', '}');
+            return;
+        }
+
         final long NONE_DIRECT_FEATURES = ReferenceDetection.mask
                 | PrettyFormat.mask
                 | NotWriteEmptyArray.mask
@@ -1642,7 +1655,97 @@ public abstract class JSONWriter
         write0('}');
     }
 
-    public abstract void write(JSONObject map);
+    public void write(JSONObject map) {
+        if (map == null) {
+            this.writeNull();
+            return;
+        }
+
+        if (map.isEmpty()) {
+            writeRaw('{', '}');
+            return;
+        }
+
+        final long NONE_DIRECT_FEATURES = ReferenceDetection.mask
+                | NotWriteEmptyArray.mask
+                | NotWriteDefaultValue.mask;
+
+        if ((context.features & NONE_DIRECT_FEATURES) != 0) {
+            ObjectWriter objectWriter = context.getObjectWriter(map.getClass());
+            objectWriter.write(this, map, null, null, 0);
+            return;
+        }
+
+        startObject();
+
+        boolean first = true;
+        for (Map.Entry entry : map.entrySet()) {
+            Object value = entry.getValue();
+            if (value == null && (context.features & WriteMapNullValue.mask) == 0) {
+                continue;
+            }
+
+            if (!first) {
+                writeComma();
+            }
+
+            first = false;
+            Object key = entry.getKey();
+            if (key instanceof String) {
+                writeString((String) key);
+            } else {
+                writeAny(key);
+            }
+
+            writeColon();
+
+            if (value == null) {
+                writeNull();
+                continue;
+            }
+
+            Class<?> valueClass = value.getClass();
+            if (valueClass == String.class) {
+                writeString((String) value);
+                continue;
+            }
+
+            if (valueClass == Integer.class) {
+                writeInt32((Integer) value);
+                continue;
+            }
+
+            if (valueClass == Long.class) {
+                writeInt64((Long) value);
+                continue;
+            }
+
+            if (valueClass == Boolean.class) {
+                writeBool((Boolean) value);
+                continue;
+            }
+
+            if (valueClass == BigDecimal.class) {
+                writeDecimal((BigDecimal) value, 0, null);
+                continue;
+            }
+
+            if (valueClass == JSONArray.class) {
+                write((JSONArray) value);
+                continue;
+            }
+
+            if (valueClass == JSONObject.class) {
+                write((JSONObject) value);
+                continue;
+            }
+
+            ObjectWriter objectWriter = context.getObjectWriter(valueClass, valueClass);
+            objectWriter.write(this, value, null, null, 0);
+        }
+
+        endObject();
+    }
 
     public void writeAny(Object value) {
         if (value == null) {
@@ -2213,7 +2316,19 @@ public abstract class JSONWriter
          * SortedMap and derived classes do not need to do this.
          * @since 2.0.48
          */
-        SortMapEntriesByKeys(1L << 41);
+        SortMapEntriesByKeys(1L << 41),
+
+        /**
+         * JSON formatting support using 4 spaces for indentation
+         * @since 2.0.54
+         */
+        PrettyFormatWith2Space(1L << 42),
+
+        /**
+         * JSON formatting support using 4 spaces for indentation
+         * @since 2.0.54
+         */
+        PrettyFormatWith4Space(1L << 43);
 
         public final long mask;
 
@@ -2583,23 +2698,23 @@ public abstract class JSONWriter
      * @deprecated
      */
     public final void incrementIndent() {
-        indent++;
+        level++;
     }
 
     /**
      * @deprecated
      */
     public final void decrementIdent() {
-        indent--;
+        level--;
     }
 
     /**
      * @deprecated
      */
     public void println() {
-        writeChar('\n');
-        for (int i = 0; i < indent; ++i) {
-            writeChar('\t');
+        writeRaw('\n');
+        for (int i = 0; i < level; ++i) {
+            writeRaw('\t');
         }
     }
 
diff --git a/core/src/main/java/com/alibaba/fastjson2/JSONWriterJSONB.java b/core/src/main/java/com/alibaba/fastjson2/JSONWriterJSONB.java
index 9446c7952..ebb27c3b0 100644
--- a/core/src/main/java/com/alibaba/fastjson2/JSONWriterJSONB.java
+++ b/core/src/main/java/com/alibaba/fastjson2/JSONWriterJSONB.java
@@ -85,7 +85,7 @@ final class JSONWriterJSONB
         level++;
         int off = this.off;
         if (off == bytes.length) {
-            ensureCapacity(off + 1);
+            grow0(off + 1);
         }
         bytes[off] = BC_OBJECT;
         this.off = off + 1;
@@ -96,7 +96,7 @@ final class JSONWriterJSONB
         level--;
         int off = this.off;
         if (off == bytes.length) {
-            ensureCapacity(off + 1);
+            grow0(off + 1);
         }
         bytes[off] = BC_OBJECT_END;
         this.off = off + 1;
@@ -115,7 +115,7 @@ final class JSONWriterJSONB
 
         int off = this.off;
         if (off == bytes.length) {
-            ensureCapacity(off + 1);
+            grow0(off + 1);
         }
 
         boolean tinyInt = size <= ARRAY_FIX_LEN;
@@ -130,7 +130,7 @@ final class JSONWriterJSONB
     public void startArray(int size) {
         int off = this.off;
         if (off + 1 >= bytes.length) {
-            ensureCapacity(off + 2);
+            grow0(off + 2);
         }
 
         boolean tinyInt = size <= ARRAY_FIX_LEN;
@@ -145,7 +145,7 @@ final class JSONWriterJSONB
     public void startArray0() {
         int off = this.off;
         if (off == bytes.length) {
-            ensureCapacity(off + 1);
+            grow0(off + 1);
         }
 
         bytes[off] = BC_ARRAY_FIX_MIN;
@@ -156,7 +156,7 @@ final class JSONWriterJSONB
     public void startArray1() {
         int off = this.off;
         if (off == bytes.length) {
-            ensureCapacity(off + 1);
+            grow0(off + 1);
         }
 
         bytes[off] = (byte) (BC_ARRAY_FIX_MIN + 1);
@@ -167,7 +167,7 @@ final class JSONWriterJSONB
     public void startArray2() {
         int off = this.off;
         if (off == bytes.length) {
-            ensureCapacity(off + 1);
+            grow0(off + 1);
         }
 
         bytes[off] = (byte) (BC_ARRAY_FIX_MIN + 2);
@@ -178,7 +178,7 @@ final class JSONWriterJSONB
     public void startArray3() {
         int off = this.off;
         if (off == bytes.length) {
-            ensureCapacity(off + 1);
+            grow0(off + 1);
         }
 
         bytes[off] = (byte) (BC_ARRAY_FIX_MIN + 3);
@@ -189,7 +189,7 @@ final class JSONWriterJSONB
     public void startArray4() {
         int off = this.off;
         if (off == bytes.length) {
-            ensureCapacity(off + 1);
+            grow0(off + 1);
         }
 
         bytes[off] = (byte) (BC_ARRAY_FIX_MIN + 4);
@@ -200,7 +200,7 @@ final class JSONWriterJSONB
     public void startArray5() {
         int off = this.off;
         if (off == bytes.length) {
-            ensureCapacity(off + 1);
+            grow0(off + 1);
         }
 
         bytes[off] = (byte) (BC_ARRAY_FIX_MIN + 5);
@@ -211,7 +211,7 @@ final class JSONWriterJSONB
     public void startArray6() {
         int off = this.off;
         if (off == bytes.length) {
-            ensureCapacity(off + 1);
+            grow0(off + 1);
         }
 
         bytes[off] = (byte) (BC_ARRAY_FIX_MIN + 6);
@@ -222,7 +222,7 @@ final class JSONWriterJSONB
     public void startArray7() {
         int off = this.off;
         if (off == bytes.length) {
-            ensureCapacity(off + 1);
+            grow0(off + 1);
         }
 
         bytes[off] = (byte) (BC_ARRAY_FIX_MIN + 7);
@@ -233,7 +233,7 @@ final class JSONWriterJSONB
     public void startArray8() {
         int off = this.off;
         if (off == bytes.length) {
-            ensureCapacity(off + 1);
+            grow0(off + 1);
         }
 
         bytes[off] = (byte) (BC_ARRAY_FIX_MIN + 8);
@@ -244,7 +244,7 @@ final class JSONWriterJSONB
     public void startArray9() {
         int off = this.off;
         if (off == bytes.length) {
-            ensureCapacity(off + 1);
+            grow0(off + 1);
         }
 
         bytes[off] = (byte) (BC_ARRAY_FIX_MIN + 9);
@@ -255,7 +255,7 @@ final class JSONWriterJSONB
     public void startArray10() {
         int off = this.off;
         if (off == bytes.length) {
-            ensureCapacity(off + 1);
+            grow0(off + 1);
         }
 
         bytes[off] = (byte) (BC_ARRAY_FIX_MIN + 10);
@@ -266,7 +266,7 @@ final class JSONWriterJSONB
     public void startArray11() {
         int off = this.off;
         if (off == bytes.length) {
-            ensureCapacity(off + 1);
+            grow0(off + 1);
         }
 
         bytes[off] = (byte) (BC_ARRAY_FIX_MIN + 11);
@@ -277,7 +277,7 @@ final class JSONWriterJSONB
     public void startArray12() {
         int off = this.off;
         if (off == bytes.length) {
-            ensureCapacity(off + 1);
+            grow0(off + 1);
         }
 
         bytes[off] = (byte) (BC_ARRAY_FIX_MIN + 12);
@@ -288,7 +288,7 @@ final class JSONWriterJSONB
     public void startArray13() {
         int off = this.off;
         if (off == bytes.length) {
-            ensureCapacity(off + 1);
+            grow0(off + 1);
         }
 
         bytes[off] = (byte) (BC_ARRAY_FIX_MIN + 13);
@@ -299,7 +299,7 @@ final class JSONWriterJSONB
     public void startArray14() {
         int off = this.off;
         if (off == bytes.length) {
-            ensureCapacity(off + 1);
+            grow0(off + 1);
         }
 
         bytes[off] = (byte) (BC_ARRAY_FIX_MIN + 14);
@@ -310,7 +310,7 @@ final class JSONWriterJSONB
     public void startArray15() {
         int off = this.off;
         if (off == bytes.length) {
-            ensureCapacity(off + 1);
+            grow0(off + 1);
         }
 
         bytes[off] = (byte) (BC_ARRAY_FIX_MIN + 15);
@@ -320,7 +320,7 @@ final class JSONWriterJSONB
     @Override
     public void writeRaw(byte b) {
         if (off == bytes.length) {
-            ensureCapacity(off + 1);
+            grow0(off + 1);
         }
         bytes[off++] = b;
     }
@@ -328,7 +328,7 @@ final class JSONWriterJSONB
     @Override
     public void writeChar(char ch) {
         if (off == bytes.length) {
-            ensureCapacity(off + 1);
+            grow0(off + 1);
         }
         bytes[off++] = BC_CHAR;
         writeInt32(ch);
@@ -342,7 +342,7 @@ final class JSONWriterJSONB
     @Override
     public void writeNull() {
         if (off == bytes.length) {
-            ensureCapacity(off + 1);
+            grow0(off + 1);
         }
         bytes[off++] = BC_NULL;
     }
@@ -350,7 +350,7 @@ final class JSONWriterJSONB
     @Override
     public void writeStringNull() {
         if (off == bytes.length) {
-            ensureCapacity(off + 1);
+            grow0(off + 1);
         }
         bytes[off++] = BC_NULL;
     }
@@ -479,7 +479,7 @@ final class JSONWriterJSONB
     }
 
     @Override
-    public void writeString(char[] chars, int off, int len, boolean quote) {
+    public void writeString(char[] chars, int coff, int len, boolean quote) {
         if (chars == null) {
             writeNull();
             return;
@@ -487,26 +487,29 @@ final class JSONWriterJSONB
 
         boolean ascii = true;
         for (int i = 0; i < len; ++i) {
-            if (chars[i + off] > 0x00FF) {
+            if (chars[i + coff] > 0x00FF) {
                 ascii = false;
                 break;
             }
         }
 
         if (ascii) {
+            int off = this.off;
+            byte[] bytes = this.bytes;
             if (len <= STR_ASCII_FIX_LEN) {
-                bytes[this.off++] = (byte) (len + BC_STR_ASCII_FIX_MIN);
+                bytes[off++] = (byte) (len + BC_STR_ASCII_FIX_MIN);
             } else {
-                bytes[this.off++] = BC_STR_ASCII;
-                writeInt32(len);
+                bytes[off++] = BC_STR_ASCII;
+                off += writeInt32(bytes, off, len);
             }
             for (int i = 0; i < len; ++i) {
-                bytes[this.off++] = (byte) chars[off + i];
+                bytes[off++] = (byte) chars[coff + i];
             }
+            this.off = off;
             return;
         }
 
-        writeString(new String(chars, off, len));
+        writeString(new String(chars, coff, len));
     }
 
     public void writeStringLatin1(final byte[] value) {
@@ -523,7 +526,7 @@ final class JSONWriterJSONB
                 + 1;
 
         if (minCapacity - bytes.length > 0) {
-            ensureCapacity(minCapacity);
+            grow0(minCapacity);
         }
 
         final byte[] bytes = this.bytes;
@@ -574,10 +577,11 @@ final class JSONWriterJSONB
         int off = this.off;
         boolean ascii = true;
         int strlen = chars.length;
+        byte[] bytes = this.bytes;
         if (chars.length < STR_ASCII_FIX_LEN) {
             int minCapacity = off + 1 + strlen;
-            if (minCapacity - bytes.length > 0) {
-                ensureCapacity(minCapacity);
+            if (minCapacity > bytes.length) {
+                bytes = grow(minCapacity);
             }
 
             bytes[off++] = (byte) (strlen + BC_STR_ASCII_FIX_MIN);
@@ -626,8 +630,8 @@ final class JSONWriterJSONB
                 + 5 /*max str len*/
                 + 1;
 
-        if (minCapacity - bytes.length > 0) {
-            ensureCapacity(minCapacity);
+        if (minCapacity > bytes.length) {
+            bytes = grow(minCapacity);
         }
 
         if (ascii) {
@@ -645,7 +649,10 @@ final class JSONWriterJSONB
         } else {
             int maxSize = chars.length * 3;
             int lenByteCnt = sizeOfInt(maxSize);
-            ensureCapacity(off + maxSize + lenByteCnt + 1);
+            minCapacity = off + maxSize + lenByteCnt + 1;
+            if (minCapacity > bytes.length) {
+                bytes = grow(minCapacity);
+            }
             int result = IOUtils.encodeUTF8(chars, 0, chars.length, bytes, off + lenByteCnt + 1);
 
             int utf8len = result - off - lenByteCnt - 1;
@@ -653,7 +660,6 @@ final class JSONWriterJSONB
             if (lenByteCnt != utf8lenByteCnt) {
                 System.arraycopy(bytes, off + lenByteCnt + 1, bytes, off + utf8lenByteCnt + 1, utf8len);
             }
-            final byte[] bytes = this.bytes;
             bytes[off++] = BC_STR_UTF8;
             if (utf8len >= BC_INT32_NUM_MIN && utf8len <= BC_INT32_NUM_MAX) {
                 bytes[off++] = (byte) utf8len;
@@ -676,31 +682,32 @@ final class JSONWriterJSONB
             return;
         }
 
+        int off = this.off;
+        byte[] bytes = this.bytes;
         boolean ascii = true;
 
         if (len < STR_ASCII_FIX_LEN) {
-            final int mark = this.off;
-
-            int minCapacity = this.off + 1 + len;
-            if (minCapacity - bytes.length > 0) {
-                ensureCapacity(minCapacity);
+            int minCapacity = off + 1 + len;
+            if (minCapacity > bytes.length) {
+                bytes = grow(minCapacity);
             }
 
-            bytes[this.off++] = (byte) (len + BC_STR_ASCII_FIX_MIN);
+            bytes[off++] = (byte) (len + BC_STR_ASCII_FIX_MIN);
             for (int i = charsOff; i < len; i++) {
                 char ch = chars[i];
                 if (ch > 0x00FF) {
                     ascii = false;
                     break;
                 }
-                bytes[this.off++] = (byte) ch;
+                bytes[off++] = (byte) ch;
             }
 
             if (ascii) {
+                this.off = off;
                 return;
             }
 
-            this.off = mark;
+            off = this.off;
         }
 
         {
@@ -727,54 +734,56 @@ final class JSONWriterJSONB
         }
 
         int minCapacity = (ascii ? len : len * 3)
-                + this.off
+                + off
                 + 5 /*max str len*/
                 + 1;
 
-        if (minCapacity - bytes.length > 0) {
-            ensureCapacity(minCapacity);
+        if (minCapacity > bytes.length) {
+            bytes = grow(minCapacity);
         }
 
         if (ascii) {
-            byte[] bytes = this.bytes;
             if (len <= STR_ASCII_FIX_LEN) {
-                bytes[this.off++] = (byte) (len + BC_STR_ASCII_FIX_MIN);
+                bytes[off++] = (byte) (len + BC_STR_ASCII_FIX_MIN);
             } else if (len <= INT32_BYTE_MAX) {
-                int off = this.off;
                 bytes[off] = BC_STR_ASCII;
                 bytes[off + 1] = (byte) (BC_INT32_BYTE_ZERO + (len >> 8));
                 bytes[off + 2] = (byte) (len);
-                this.off += 3;
+                off += 3;
             } else {
-                bytes[this.off++] = BC_STR_ASCII;
-                writeInt32(len);
+                bytes[off++] = BC_STR_ASCII;
+                off += writeInt32(bytes, off, len);
             }
             for (int i = 0; i < chars.length; i++) {
-                bytes[this.off++] = (byte) chars[i];
+                bytes[off++] = (byte) chars[i];
             }
         } else {
             int maxSize = chars.length * 3;
             int lenByteCnt = sizeOfInt(maxSize);
-            ensureCapacity(this.off + maxSize + lenByteCnt + 1);
-            int result = IOUtils.encodeUTF8(chars, 0, chars.length, bytes, this.off + lenByteCnt + 1);
+            minCapacity = off + maxSize + lenByteCnt + 1;
+            if (minCapacity > bytes.length) {
+                bytes = grow(minCapacity);
+            }
+            int result = IOUtils.encodeUTF8(chars, 0, chars.length, bytes, off + lenByteCnt + 1);
 
-            int utf8len = result - this.off - lenByteCnt - 1;
+            int utf8len = result - off - lenByteCnt - 1;
             int utf8lenByteCnt = sizeOfInt(utf8len);
             if (lenByteCnt != utf8lenByteCnt) {
-                System.arraycopy(bytes, this.off + lenByteCnt + 1, bytes, this.off + utf8lenByteCnt + 1, utf8len);
+                System.arraycopy(bytes, off + lenByteCnt + 1, bytes, off + utf8lenByteCnt + 1, utf8len);
             }
-            bytes[this.off++] = BC_STR_UTF8;
+            bytes[off++] = BC_STR_UTF8;
             if (utf8len >= BC_INT32_NUM_MIN && utf8len <= BC_INT32_NUM_MAX) {
-                bytes[this.off++] = (byte) utf8len;
+                bytes[off++] = (byte) utf8len;
             } else if (utf8len >= INT32_BYTE_MIN && utf8len <= INT32_BYTE_MAX) {
-                bytes[this.off] = (byte) (BC_INT32_BYTE_ZERO + (utf8len >> 8));
-                bytes[this.off + 1] = (byte) (utf8len);
-                this.off += 2;
+                bytes[off] = (byte) (BC_INT32_BYTE_ZERO + (utf8len >> 8));
+                bytes[off + 1] = (byte) (utf8len);
+                off += 2;
             } else {
-                writeInt32(utf8len);
+                off += writeInt32(bytes, off, utf8len);
             }
-            this.off += utf8len;
+            off += utf8len;
         }
+        this.off = off;
     }
 
     public void writeString(String[] strings) {
@@ -815,10 +824,12 @@ final class JSONWriterJSONB
 
     @Override
     public void writeTypeName(String typeName) {
+        int off = this.off;
+        byte[] bytes = this.bytes;
         if (off == bytes.length) {
-            ensureCapacity(off + 1);
+            bytes = grow(off + 1);
         }
-        this.bytes[off++] = BC_TYPED_ANY;
+        bytes[off++] = BC_TYPED_ANY;
 
         long hash = Fnv.hashCode64(typeName);
 
@@ -839,12 +850,13 @@ final class JSONWriterJSONB
             symbols.put(hash, symbol = symbolIndex++);
         } else {
             if (off == bytes.length) {
-                ensureCapacity(off + 1);
+                bytes = grow(off + 1);
             }
 
-            writeInt32(symbol);
+            this.off = off + writeInt32(bytes, off, symbol);
             return;
         }
+        this.off = off;
 
         writeString(typeName);
         writeInt32(symbol);
@@ -897,25 +909,23 @@ final class JSONWriterJSONB
         System.arraycopy(typeName, 0, bytes, off, typeName.length);
         off += typeName.length;
         if (symbol >= BC_INT32_NUM_MIN && symbol <= BC_INT32_NUM_MAX) {
-            bytes[off] = (byte) symbol;
-            this.off = off + 1;
+            bytes[off++] = (byte) symbol;
         } else {
-            this.off = off;
-            writeInt32(symbol);
+            off += writeInt32(bytes, off, symbol);
         }
-
+        this.off = off;
         return false;
     }
 
     private boolean writeTypeNameSymbol(int symbol) {
         int off = this.off;
-        if (off + 2 >= bytes.length) {
-            ensureCapacity(off + 2);
+        byte[] bytes = this.bytes;
+        if (off + 7 > bytes.length) {
+            bytes = grow(off + 7);
         }
 
-        this.bytes[off] = BC_TYPED_ANY;
-        this.off = off + 1;
-        writeInt32(-symbol);
+        bytes[off++] = BC_TYPED_ANY;
+        this.off = off + writeInt32(bytes, off, -symbol);
         return false;
     }
 
@@ -945,42 +955,45 @@ final class JSONWriterJSONB
         startArray(size);
 
         if (STRING_VALUE != null && STRING_CODER != null) {
-            int mark = off;
+            int off = this.off;
+            byte[] bytes = this.bytes;
             final int LATIN = 0;
             boolean latinAll = true;
             for (int i = 0; i < list.size(); i++) {
                 String str = list.get(i);
                 if (str == null) {
-                    writeNull();
+                    if (off == bytes.length) {
+                        bytes = grow(off + 1);
+                    }
+                    bytes[off++] = BC_NULL;
                     continue;
                 }
                 int coder = STRING_CODER.applyAsInt(str);
                 if (coder != LATIN) {
                     latinAll = false;
-                    off = mark;
                     break;
                 }
                 int strlen = str.length();
                 if (off + strlen + 6 > bytes.length) {
-                    ensureCapacity(off + strlen + 6);
+                    bytes = grow(off + strlen + 6);
                 }
                 if (strlen <= STR_ASCII_FIX_LEN) {
                     bytes[off++] = (byte) (strlen + BC_STR_ASCII_FIX_MIN);
                 } else if (strlen <= INT32_BYTE_MAX) {
-                    int off = this.off;
                     bytes[off] = BC_STR_ASCII;
                     bytes[off + 1] = (byte) (BC_INT32_BYTE_ZERO + (strlen >> 8));
                     bytes[off + 2] = (byte) (strlen);
-                    this.off += 3;
+                    off += 3;
                 } else {
                     bytes[off++] = BC_STR_ASCII;
-                    writeInt32(strlen);
+                    off += writeInt32(bytes, off, strlen);
                 }
                 byte[] value = STRING_VALUE.apply(str);
                 System.arraycopy(value, 0, bytes, off, value.length);
                 off += strlen;
             }
             if (latinAll) {
+                this.off = off;
                 return;
             }
         }
@@ -1039,11 +1052,10 @@ final class JSONWriterJSONB
         int off = this.off;
         final int strlen = value.length;
         int minCapacity = off + strlen + 6;
-        if (minCapacity >= bytes.length) {
-            ensureCapacity(minCapacity);
+        byte[] bytes = this.bytes;
+        if (minCapacity > bytes.length) {
+            bytes = grow(minCapacity);
         }
-
-        final byte[] bytes = this.bytes;
         bytes[off++] = JDKUtils.BIG_ENDIAN ? BC_STR_UTF16BE : BC_STR_UTF16LE;
         off += writeInt32(bytes, off, strlen);
         System.arraycopy(value, 0, bytes, off, strlen);
@@ -1071,11 +1083,10 @@ final class JSONWriterJSONB
         boolean utf16 = value.length != 0 && (asciiCount == 0 || (check_cnt >> 1) / asciiCount >= 3); // utf16字符占比>=1/3
         int off = this.off;
         int minCapacity = off + 6 + value.length * 2 + 1;
-        if (minCapacity >= this.bytes.length) {
-            ensureCapacity(minCapacity);
+        byte[] bytes = this.bytes;
+        if (minCapacity > bytes.length) {
+            bytes = grow(minCapacity);
         }
-
-        final byte[] bytes = this.bytes;
         if (!utf16) {
             int maxSize = value.length + (value.length >> 2);
             int lenByteCnt = sizeOfInt(maxSize);
@@ -1132,29 +1143,37 @@ final class JSONWriterJSONB
 
     void ensureCapacity(int minCapacity) {
         if (minCapacity >= bytes.length) {
-            int oldCapacity = bytes.length;
-            int newCapacity = oldCapacity + (oldCapacity >> 1);
-            if (newCapacity - minCapacity < 0) {
-                newCapacity = minCapacity;
-            }
-            if (newCapacity > maxArraySize) {
-                throw new OutOfMemoryError("try enabling LargeObject feature instead");
-            }
+            grow0(minCapacity);
+        }
+    }
 
-            // minCapacity is usually close to size, so this is a win:
-            bytes = Arrays.copyOf(bytes, newCapacity);
+    private byte[] grow(int minCapacity) {
+        grow0(minCapacity);
+        return bytes;
+    }
+
+    private void grow0(int minCapacity) {
+        int oldCapacity = bytes.length;
+        int newCapacity = oldCapacity + (oldCapacity >> 1);
+        if (newCapacity - minCapacity < 0) {
+            newCapacity = minCapacity;
+        }
+        if (newCapacity > maxArraySize) {
+            throw new OutOfMemoryError("try enabling LargeObject feature instead");
         }
+
+        // minCapacity is usually close to size, so this is a win:
+        bytes = Arrays.copyOf(bytes, newCapacity);
     }
 
     @Override
     public void writeMillis(long millis) {
         int off = this.off;
         int minCapacity = off + 9;
-        if (minCapacity >= bytes.length) {
-            ensureCapacity(minCapacity);
+        byte[] bytes = this.bytes;
+        if (minCapacity > bytes.length) {
+            bytes = grow(minCapacity);
         }
-
-        final byte[] bytes = this.bytes;
         if (millis % 1000 == 0) {
             long seconds = (millis / 1000);
             if (seconds >= Integer.MIN_VALUE && seconds <= Integer.MAX_VALUE) {
@@ -1200,11 +1219,10 @@ final class JSONWriterJSONB
     @Override
     public void writeInt64(Long i) {
         int minCapacity = off + 9;
+        byte[] bytes = this.bytes;
         if (minCapacity > bytes.length) {
-            ensureCapacity(minCapacity);
+            bytes = grow(minCapacity);
         }
-
-        final byte[] bytes = this.bytes;
         int off = this.off;
         int size;
         if (i == null) {
@@ -1245,11 +1263,10 @@ final class JSONWriterJSONB
     @Override
     public void writeInt64(long val) {
         int minCapacity = off + 9;
+        byte[] bytes = this.bytes;
         if (minCapacity > bytes.length) {
-            ensureCapacity(minCapacity);
+            bytes = grow(minCapacity);
         }
-
-        final byte[] bytes = this.bytes;
         int off = this.off;
         int size;
         if (val >= INT64_NUM_LOW_VALUE && val <= INT64_NUM_HIGH_VALUE) {
@@ -1295,11 +1312,10 @@ final class JSONWriterJSONB
 
         int off = this.off;
         int minCapacity = off + size * 9 + 5;
-        if (minCapacity >= bytes.length) {
-            ensureCapacity(minCapacity);
+        byte[] bytes = this.bytes;
+        if (minCapacity > bytes.length) {
+            bytes = grow(minCapacity);
         }
-
-        final byte[] bytes = this.bytes;
         if (size <= ARRAY_FIX_LEN) {
             bytes[off++] = (byte) (BC_ARRAY_FIX_MIN + size);
         } else {
@@ -1362,11 +1378,10 @@ final class JSONWriterJSONB
 
         int off = this.off;
         int minCapacity = off + size * 9 + 5;
-        if (minCapacity >= bytes.length) {
-            ensureCapacity(minCapacity);
+        byte[] bytes = this.bytes;
+        if (minCapacity > bytes.length) {
+            bytes = grow(minCapacity);
         }
-
-        final byte[] bytes = this.bytes;
         if (size <= ARRAY_FIX_LEN) {
             bytes[off++] = (byte) (BC_ARRAY_FIX_MIN + size);
         } else {
@@ -1435,11 +1450,10 @@ final class JSONWriterJSONB
     public void writeFloat(float value) {
         int off = this.off;
         int minCapacity = off + 5;
-        if (minCapacity >= bytes.length) {
-            ensureCapacity(minCapacity);
+        byte[] bytes = this.bytes;
+        if (minCapacity > bytes.length) {
+            bytes = grow(minCapacity);
         }
-
-        final byte[] bytes = this.bytes;
         int i = (int) value;
         if (i == value && value >= BC_INT32_NUM_MIN && value <= BC_INT32_NUM_MAX) {
             bytes[off] = BC_FLOAT_INT;
@@ -1473,16 +1487,13 @@ final class JSONWriterJSONB
 
     @Override
     public void writeDouble(double value) {
-        if (value == 0) {
-            ensureCapacity(off + 1);
-            bytes[off++] = BC_DOUBLE_NUM_0;
-            return;
-        }
-
         int off = this.off;
-        if (value == 1) {
-            ensureCapacity(off + 1);
-            bytes[off] = BC_DOUBLE_NUM_1;
+        byte[] bytes = this.bytes;
+        if (value == 0 || value == 1) {
+            if (off == bytes.length) {
+                bytes = grow(off + 1);
+            }
+            bytes[off] = value == 0 ? BC_DOUBLE_NUM_0 : BC_DOUBLE_NUM_1;
             this.off = off + 1;
             return;
         }
@@ -1490,7 +1501,9 @@ final class JSONWriterJSONB
         if (value >= Integer.MIN_VALUE && value <= Integer.MAX_VALUE) {
             long longValue = (long) value;
             if (longValue == value) {
-                ensureCapacity(off + 1);
+                if (off == bytes.length) {
+                    bytes = grow(off + 1);
+                }
                 bytes[off] = BC_DOUBLE_LONG;
                 this.off = off + 1;
                 writeInt64(longValue);
@@ -1498,8 +1511,9 @@ final class JSONWriterJSONB
             }
         }
 
-        ensureCapacity(off + 9);
-        final byte[] bytes = this.bytes;
+        if (off + 9 > bytes.length) {
+            bytes = grow(off + 9);
+        }
         bytes[off] = BC_DOUBLE;
         long i = Double.doubleToLongBits(value);
         UNSAFE.putLong(
@@ -1543,26 +1557,22 @@ final class JSONWriterJSONB
             return;
         }
 
+        int off = this.off;
+        byte[] bytes = this.bytes;
         // inline startArray(value.length);
         int size = values.length;
-        if (off == bytes.length) {
-            ensureCapacity(off + 1);
+        int minCapacity = off + 6 + values.length * 5;
+        if (minCapacity > bytes.length) {
+            bytes = grow(minCapacity);
         }
 
         if (size <= ARRAY_FIX_LEN) {
             bytes[off++] = (byte) (BC_ARRAY_FIX_MIN + size);
         } else {
             bytes[off++] = BC_ARRAY;
-            writeInt32(size);
+            off += writeInt32(bytes, off, size);
         }
 
-        int off = this.off;
-        int minCapacity = off + values.length * 5;
-        if (minCapacity - bytes.length > 0) {
-            ensureCapacity(minCapacity);
-        }
-
-        final byte[] bytes = this.bytes;
         for (int i = 0; i < values.length; i++) {
             int val = values[i];
             if (val >= BC_INT32_NUM_MIN && val <= BC_INT32_NUM_MAX) {
@@ -1602,25 +1612,21 @@ final class JSONWriterJSONB
             return;
         }
 
+        int off = this.off;
         int size = values.length;
-        if (off == bytes.length) {
-            ensureCapacity(off + 1);
+        int minCapacity = off + 6 + values.length * 2;
+        byte[] bytes = this.bytes;
+        if (minCapacity > bytes.length) {
+            bytes = grow(minCapacity);
         }
 
         if (size <= ARRAY_FIX_LEN) {
             bytes[off++] = (byte) (BC_ARRAY_FIX_MIN + size);
         } else {
             bytes[off++] = BC_ARRAY;
-            writeInt32(size);
+            off += writeInt32(bytes, off, size);
         }
 
-        int off = this.off;
-        int minCapacity = off + values.length * 2;
-        if (minCapacity - bytes.length > 0) {
-            ensureCapacity(minCapacity);
-        }
-
-        final byte[] bytes = this.bytes;
         for (int val : values) {
             if (val < BC_INT32_NUM_MIN || val > BC_INT32_NUM_MAX) {
                 bytes[off++] = (byte) (BC_INT32_BYTE_ZERO + (val >> 8));
@@ -1633,12 +1639,10 @@ final class JSONWriterJSONB
     @Override
     public void writeInt8(byte val) {
         int off = this.off;
-        int minCapacity = off + 2;
-        if (minCapacity - bytes.length > 0) {
-            ensureCapacity(minCapacity);
+        byte[] bytes = this.bytes;
+        if (off + 2 > bytes.length) {
+            bytes = grow(off + 2);
         }
-
-        final byte[] bytes = this.bytes;
         bytes[off] = BC_INT8;
         bytes[off + 1] = val;
         this.off = off + 2;
@@ -1647,11 +1651,10 @@ final class JSONWriterJSONB
     @Override
     public void writeInt16(short val) {
         int off = this.off;
-        int minCapacity = off + 3;
-        if (minCapacity >= bytes.length) {
-            ensureCapacity(minCapacity);
+        byte[] bytes = this.bytes;
+        if (off + 3 > bytes.length) {
+            bytes = grow(off + 3);
         }
-        final byte[] bytes = this.bytes;
         bytes[off] = BC_INT16;
         bytes[off + 1] = (byte) (val >>> 8);
         bytes[off + 2] = (byte) val;
@@ -1672,27 +1675,23 @@ final class JSONWriterJSONB
                             : e.name()
             );
         } else {
-            int val = e.ordinal();
-            if (val <= BC_INT32_NUM_MAX) {
-                if (off == bytes.length) {
-                    ensureCapacity(off + 1);
-                }
-
-                bytes[off++] = (byte) val;
-                return;
+            int ordinal = e.ordinal();
+            byte[] bytes = this.bytes;
+            int off = this.off;
+            if (off + 5 > bytes.length) {
+                bytes = grow(off + 5);
             }
-            writeInt32(val);
+            this.off = off + writeInt32(bytes, off, ordinal);
         }
     }
 
     @Override
     public void writeInt32(Integer i) {
         int minCapacity = off + 5;
-        if (minCapacity >= bytes.length) {
-            ensureCapacity(minCapacity);
+        byte[] bytes = this.bytes;
+        if (minCapacity > bytes.length) {
+            bytes = grow(minCapacity);
         }
-
-        final byte[] bytes = this.bytes;
         int size;
         int off = this.off;
         if (i == null) {
@@ -1732,11 +1731,10 @@ final class JSONWriterJSONB
     @Override
     public void writeInt32(int val) {
         int minCapacity = off + 5;
-        if (minCapacity >= bytes.length) {
-            ensureCapacity(minCapacity);
+        byte[] bytes = this.bytes;
+        if (minCapacity > bytes.length) {
+            bytes = grow(minCapacity);
         }
-
-        final byte[] bytes = this.bytes;
         int size;
         int off = this.off;
         if (val >= BC_INT32_NUM_MIN && val <= BC_INT32_NUM_MAX) {
@@ -1774,11 +1772,10 @@ final class JSONWriterJSONB
 
         int off = this.off;
         int minCapacity = off + size * 5 + 5;
-        if (minCapacity >= bytes.length) {
-            ensureCapacity(minCapacity);
+        byte[] bytes = this.bytes;
+        if (minCapacity > bytes.length) {
+            bytes = grow(minCapacity);
         }
-
-        final byte[] bytes = this.bytes;
         if (size <= ARRAY_FIX_LEN) {
             bytes[off++] = (byte) (BC_ARRAY_FIX_MIN + size);
         } else {
@@ -1842,6 +1839,38 @@ final class JSONWriterJSONB
         }
     }
 
+    public static int writeInt64(byte[] bytes, int off, long val) {
+        if (val >= INT64_NUM_LOW_VALUE && val <= INT64_NUM_HIGH_VALUE) {
+            bytes[off] = (byte) (BC_INT64_NUM_MIN + (val - INT64_NUM_LOW_VALUE));
+            return 1;
+        } else if (val >= INT64_BYTE_MIN && val <= INT64_BYTE_MAX) {
+            bytes[off] = (byte) (BC_INT64_BYTE_ZERO + (val >> 8));
+            bytes[off + 1] = (byte) (val);
+            return 2;
+        } else if (val >= INT64_SHORT_MIN && val <= INT64_SHORT_MAX) {
+            bytes[off] = (byte) (BC_INT64_SHORT_ZERO + (val >> 16));
+            bytes[off + 1] = (byte) (val >> 8);
+            bytes[off + 2] = (byte) (val);
+            return 3;
+        } else if (val >= Integer.MIN_VALUE && val <= Integer.MAX_VALUE) {
+            bytes[off] = BC_INT64_INT;
+            UNSAFE.putInt(
+                    bytes,
+                    ARRAY_BYTE_BASE_OFFSET + off + 1,
+                    BIG_ENDIAN ? (int) val : Integer.reverseBytes((int) val)
+            );
+            return 5;
+        } else {
+            bytes[off] = BC_INT64;
+            UNSAFE.putLong(
+                    bytes,
+                    ARRAY_BYTE_BASE_OFFSET + off + 1,
+                    BIG_ENDIAN ? val : Long.reverseBytes(val)
+            );
+            return 9;
+        }
+    }
+
     @Override
     public void writeArrayNull() {
         if (off == bytes.length) {
@@ -1857,48 +1886,46 @@ final class JSONWriterJSONB
     }
 
     @Override
-    public void writeRaw(byte[] bytes) {
-        int minCapacity = this.off + bytes.length;
-        if (minCapacity - this.bytes.length > 0) {
-            ensureCapacity(minCapacity);
+    public void writeRaw(byte[] raw) {
+        int off = this.off;
+        int minCapacity = off + raw.length;
+        byte[] bytes = this.bytes;
+        if (minCapacity > bytes.length) {
+            bytes = grow(minCapacity);
         }
-        System.arraycopy(bytes, 0, this.bytes, off, bytes.length);
-        off += bytes.length;
+        System.arraycopy(raw, 0, bytes, off, raw.length);
+        this.off = off + raw.length;
     }
 
     public void writeSymbol(int symbol) {
-        int minCapacity = off + 3;
-        if (minCapacity >= bytes.length) {
-            ensureCapacity(minCapacity);
+        int off = this.off;
+        int minCapacity = off + 6;
+        byte[] bytes = this.bytes;
+        if (minCapacity > bytes.length) {
+            bytes = grow(minCapacity);
         }
-
-        final byte[] bytes = this.bytes;
         bytes[off++] = BC_SYMBOL;
 
         if (symbol >= BC_INT32_NUM_MIN && symbol <= BC_INT32_NUM_MAX) {
             bytes[off++] = (byte) symbol;
-            return;
-        }
-
-        if (symbol >= INT32_BYTE_MIN && symbol <= INT32_BYTE_MAX) {
+        } else if (symbol >= INT32_BYTE_MIN && symbol <= INT32_BYTE_MAX) {
             bytes[off] = (byte) (BC_INT32_BYTE_ZERO + (symbol >> 8));
             bytes[off + 1] = (byte) (symbol);
             off += 2;
-            return;
+        } else {
+            off += writeInt32(bytes, off, symbol);
         }
-
-        writeInt32(symbol);
+        this.off = off;
     }
 
     @Override
     public void writeNameRaw(byte[] name, long nameHash) {
         int off = this.off;
-        int minCapacity = off + name.length + 2;
-        if (minCapacity >= this.bytes.length) {
-            ensureCapacity(minCapacity);
+        int minCapacity = off + 6 + name.length;
+        byte[] bytes = this.bytes;
+        if (minCapacity > bytes.length) {
+            bytes = grow(minCapacity);
         }
-
-        final byte[] bytes = this.bytes;
         int symbol;
         if (symbolTable == null
                 || (symbol = symbolTable.getOrdinalByHashCode(nameHash)) == -1
@@ -1924,13 +1951,14 @@ final class JSONWriterJSONB
             if (!symbolExists) {
                 bytes[off++] = BC_SYMBOL;
                 System.arraycopy(name, 0, bytes, off, name.length);
-                this.off = off + name.length;
+                off += name.length;
 
                 if (symbol >= BC_INT32_NUM_MIN && symbol <= BC_INT32_NUM_MAX) {
-                    bytes[this.off++] = (byte) symbol;
+                    bytes[off++] = (byte) symbol;
                 } else {
-                    writeInt32(symbol);
+                    off += writeInt32(bytes, off, symbol);
                 }
+                this.off = off;
                 return;
             }
             symbol = -symbol;
@@ -1939,12 +1967,11 @@ final class JSONWriterJSONB
         bytes[off++] = BC_SYMBOL;
         int intValue = -symbol;
         if (intValue >= BC_INT32_NUM_MIN && intValue <= BC_INT32_NUM_MAX) {
-            bytes[off] = (byte) intValue;
-            this.off = off + 1;
+            bytes[off++] = (byte) intValue;
         } else {
-            this.off = off;
-            writeInt32(intValue);
+            off += writeInt32(bytes, off, intValue);
         }
+        this.off = off;
     }
 
     @Override
@@ -1955,9 +1982,11 @@ final class JSONWriterJSONB
         }
 
         int off = this.off;
-        ensureCapacity(off + 5);
+        byte[] bytes = this.bytes;
+        if (off + 5 > bytes.length) {
+            bytes = grow(off + 5);
+        }
 
-        final byte[] bytes = this.bytes;
         bytes[off] = BC_LOCAL_DATE;
         int year = date.getYear();
         bytes[off + 1] = (byte) (year >>> 8);
@@ -1975,17 +2004,18 @@ final class JSONWriterJSONB
         }
 
         int off = this.off;
-        ensureCapacity(off + 4);
+        byte[] bytes = this.bytes;
+        if (off + 9 > bytes.length) {
+            bytes = grow(off + 9);
+        }
 
-        final byte[] bytes = this.bytes;
         bytes[off] = BC_LOCAL_TIME;
         bytes[off + 1] = (byte) time.getHour();
         bytes[off + 2] = (byte) time.getMinute();
         bytes[off + 3] = (byte) time.getSecond();
-        this.off = off + 4;
+        off += 4;
 
-        int nano = time.getNano();
-        writeInt32(nano);
+        this.off = off + writeInt32(bytes, off, time.getNano());
     }
 
     @Override
@@ -1996,9 +2026,11 @@ final class JSONWriterJSONB
         }
 
         int off = this.off;
-        ensureCapacity(off + 8);
+        byte[] bytes = this.bytes;
+        if (off + 13 > bytes.length) {
+            bytes = grow(off + 13);
+        }
 
-        final byte[] bytes = this.bytes;
         bytes[off] = BC_LOCAL_DATETIME;
         int year = dateTime.getYear();
         bytes[off + 1] = (byte) (year >>> 8);
@@ -2008,10 +2040,9 @@ final class JSONWriterJSONB
         bytes[off + 5] = (byte) dateTime.getHour();
         bytes[off + 6] = (byte) dateTime.getMinute();
         bytes[off + 7] = (byte) dateTime.getSecond();
-        this.off = off + 8;
+        off += 8;
 
-        int nano = dateTime.getNano();
-        writeInt32(nano);
+        this.off = off + writeInt32(bytes, off, dateTime.getNano());
     }
 
     @Override
@@ -2022,9 +2053,11 @@ final class JSONWriterJSONB
         }
 
         int off = this.off;
-        ensureCapacity(off + 8);
+        byte[] bytes = this.bytes;
+        if (off + 13 > bytes.length) {
+            bytes = grow(off + 13);
+        }
 
-        final byte[] bytes = this.bytes;
         bytes[off] = BC_TIMESTAMP_WITH_TIMEZONE;
         int year = dateTime.getYear();
         bytes[off + 1] = (byte) (year >>> 8);
@@ -2034,10 +2067,9 @@ final class JSONWriterJSONB
         bytes[off + 5] = (byte) dateTime.getHour();
         bytes[off + 6] = (byte) dateTime.getMinute();
         bytes[off + 7] = (byte) dateTime.getSecond();
-        this.off = off + 8;
+        off += 8;
 
-        int nano = dateTime.getNano();
-        writeInt32(nano);
+        this.off = off + writeInt32(bytes, off, dateTime.getNano());
 
         ZoneId zoneId = dateTime.getZone();
         String zoneIdStr = zoneId.getId();
@@ -2056,9 +2088,11 @@ final class JSONWriterJSONB
         }
 
         int off = this.off;
-        ensureCapacity(off + 8);
+        byte[] bytes = this.bytes;
+        if (off + 13 > bytes.length) {
+            bytes = grow(off + 13);
+        }
 
-        final byte[] bytes = this.bytes;
         bytes[off] = BC_TIMESTAMP_WITH_TIMEZONE;
         int year = dateTime.getYear();
         bytes[off + 1] = (byte) (year >>> 8);
@@ -2068,10 +2102,9 @@ final class JSONWriterJSONB
         bytes[off + 5] = (byte) dateTime.getHour();
         bytes[off + 6] = (byte) dateTime.getMinute();
         bytes[off + 7] = (byte) dateTime.getSecond();
-        this.off = off + 8;
+        off += 8;
 
-        int nano = dateTime.getNano();
-        writeInt32(nano);
+        this.off = off + writeInt32(bytes, off, dateTime.getNano());
 
         ZoneId zoneId = dateTime.getOffset();
         String zoneIdStr = zoneId.getId();
@@ -2101,12 +2134,15 @@ final class JSONWriterJSONB
             return;
         }
 
-        ensureCapacity(off + 1);
+        int off = this.off;
+        byte[] bytes = this.bytes;
+        if (off + 15 > bytes.length) {
+            bytes = grow(off + 15);
+        }
+
         bytes[off++] = BC_TIMESTAMP;
-        long second = instant.getEpochSecond();
-        int nano = instant.getNano();
-        writeInt64(second);
-        writeInt32(nano);
+        off += writeInt64(bytes, off, instant.getEpochSecond());
+        this.off = off + writeInt32(bytes, off, instant.getNano());
     }
 
     @Override
@@ -2117,9 +2153,11 @@ final class JSONWriterJSONB
         }
 
         int off = this.off;
-        ensureCapacity(off + 18);
+        byte[] bytes = this.bytes;
+        if (off + 18 > bytes.length) {
+            bytes = grow(off + 18);
+        }
 
-        final byte[] bytes = this.bytes;
         bytes[off] = BC_BINARY;
         bytes[off + 1] = BC_INT32_NUM_16;
         long msb = value.getMostSignificantBits();
@@ -2144,38 +2182,48 @@ final class JSONWriterJSONB
             return;
         }
 
+        int off = this.off;
+        byte[] bytes = this.bytes;
         if (isInt64(value)) {
-            if (off == bytes.length) {
-                ensureCapacity(off + 1);
+            if (off + 10 > bytes.length) {
+                bytes = grow(off + 10);
             }
             bytes[off++] = BC_BIGINT_LONG;
-            long int64Value = value.longValue();
-            writeInt64(int64Value);
+            this.off = off + writeInt64(bytes, off, value.longValue());
             return;
         }
 
         byte[] valueBytes = value.toByteArray();
-        ensureCapacity(off + 5 + valueBytes.length);
+        int minCapacity = off + 5 + valueBytes.length;
+        if (minCapacity > bytes.length) {
+            bytes = grow(minCapacity);
+        }
 
         bytes[off++] = BC_BIGINT;
-        writeInt32(valueBytes.length);
+        off += writeInt32(bytes, off, valueBytes.length);
         System.arraycopy(valueBytes, 0, bytes, off, valueBytes.length);
-        off += valueBytes.length;
+        this.off = off + valueBytes.length;
     }
 
     @Override
-    public void writeBinary(byte[] bytes) {
-        if (bytes == null) {
+    public void writeBinary(byte[] binary) {
+        if (binary == null) {
             writeNull();
             return;
         }
 
-        ensureCapacity(off + 6 + bytes.length);
-        this.bytes[off++] = BC_BINARY;
-        writeInt32(bytes.length);
+        int off = this.off;
+        int len = binary.length;
+        int minCapacity = off + 6 + len;
+        byte[] bytes = this.bytes;
+        if (minCapacity > bytes.length) {
+            bytes = grow(minCapacity);
+        }
+        bytes[off++] = BC_BINARY;
+        off += writeInt32(bytes, off, len);
 
-        System.arraycopy(bytes, 0, this.bytes, off, bytes.length);
-        off += bytes.length;
+        System.arraycopy(binary, 0, bytes, off, len);
+        this.off = off + len;
     }
 
     @Override
@@ -2188,49 +2236,51 @@ final class JSONWriterJSONB
         int precision = value.precision();
         int scale = value.scale();
 
+        int off = this.off;
+        byte[] bytes = this.bytes;
+        if (off + 15 > bytes.length) {
+            bytes = grow(off + 15);
+        }
         if (precision < 19 && FIELD_DECIMAL_INT_COMPACT_OFFSET != -1) {
             long intCompact = UNSAFE.getLong(value, FIELD_DECIMAL_INT_COMPACT_OFFSET);
             if (scale == 0) {
-                ensureCapacity(off + 1);
-                this.bytes[off++] = BC_DECIMAL_LONG;
-                writeInt64(intCompact);
+                bytes[off++] = BC_DECIMAL_LONG;
+                this.off = off + writeInt64(bytes, off, intCompact);
                 return;
             }
 
-            ensureCapacity(off + 1);
-            this.bytes[off++] = BC_DECIMAL;
-            writeInt32(scale);
+            bytes[off++] = BC_DECIMAL;
+            off += writeInt32(bytes, off, scale);
             if (intCompact >= Integer.MIN_VALUE && intCompact <= Integer.MAX_VALUE) {
-                writeInt32((int) intCompact);
+                off += writeInt32(bytes, off, (int) intCompact);
             } else {
-                writeInt64(intCompact);
+                off += writeInt64(bytes, off, intCompact);
             }
+            this.off = off;
             return;
         }
 
         BigInteger unscaledValue = value.unscaledValue();
         if (scale == 0
                 && isInt64(unscaledValue)) {
-            ensureCapacity(off + 1);
-            this.bytes[off++] = BC_DECIMAL_LONG;
-            long longValue = unscaledValue.longValue();
-            writeInt64(longValue);
+            bytes[off++] = BC_DECIMAL_LONG;
+            this.off = off + writeInt64(bytes, off, unscaledValue.longValue());
             return;
         }
 
-        ensureCapacity(off + 1);
-        this.bytes[off++] = BC_DECIMAL;
-        writeInt32(scale);
+        bytes[off++] = BC_DECIMAL;
+        off += writeInt32(bytes, off, scale);
 
         if (isInt32(unscaledValue)) {
-            int intValue = unscaledValue.intValue();
-            writeInt32(intValue);
+            off += writeInt32(bytes, off, unscaledValue.intValue());
         } else if (isInt64(unscaledValue)) {
-            long longValue = unscaledValue.longValue();
-            writeInt64(longValue);
+            off += writeInt64(bytes, off, unscaledValue.longValue());
         } else {
+            this.off = off;
             writeBigInt(unscaledValue, 0);
+            return;
         }
+        this.off = off;
     }
 
     @Override
@@ -2285,9 +2335,11 @@ final class JSONWriterJSONB
             int second
     ) {
         int off = this.off;
-        ensureCapacity(off + 8);
+        byte[] bytes = this.bytes;
+        if (off + 9 > bytes.length) {
+            bytes = grow(off + 9);
+        }
 
-        final byte[] bytes = this.bytes;
         bytes[off] = BC_LOCAL_DATETIME;
         bytes[off + 1] = (byte) (year >>> 8);
         bytes[off + 2] = (byte) year;
@@ -2296,10 +2348,8 @@ final class JSONWriterJSONB
         bytes[off + 5] = (byte) hour;
         bytes[off + 6] = (byte) minute;
         bytes[off + 7] = (byte) second;
-        this.off = off + 8;
-
-        int nano = 0;
-        writeInt32(nano);
+        bytes[off + 8] = BC_INT32_NUM_0;
+        this.off = off + 9;
     }
 
     @Override
@@ -2312,9 +2362,11 @@ final class JSONWriterJSONB
             int second
     ) {
         int off = this.off;
-        ensureCapacity(off + 8);
+        byte[] bytes = this.bytes;
+        if (off + 9 > bytes.length) {
+            bytes = grow(off + 9);
+        }
 
-        final byte[] bytes = this.bytes;
         bytes[off] = BC_LOCAL_DATETIME;
         bytes[off + 1] = (byte) (year >>> 8);
         bytes[off + 2] = (byte) year;
@@ -2323,10 +2375,8 @@ final class JSONWriterJSONB
         bytes[off + 5] = (byte) hour;
         bytes[off + 6] = (byte) minute;
         bytes[off + 7] = (byte) second;
-        this.off = off + 8;
-
-        int nano = 0;
-        writeInt32(nano);
+        bytes[off + 8] = BC_INT32_NUM_0;
+        this.off = off + 9;
     }
 
     @Override
@@ -2347,9 +2397,11 @@ final class JSONWriterJSONB
     @Override
     public void writeDateYYYMMDD8(int year, int month, int dayOfMonth) {
         int off = this.off;
-        ensureCapacity(off + 5);
+        byte[] bytes = this.bytes;
+        if (off + 5 > bytes.length) {
+            bytes = grow(off + 5);
+        }
 
-        final byte[] bytes = this.bytes;
         bytes[off] = BC_LOCAL_DATE;
         bytes[off + 1] = (byte) (year >>> 8);
         bytes[off + 2] = (byte) year;
@@ -2391,9 +2443,9 @@ final class JSONWriterJSONB
     @Override
     public void writeName2Raw(long name) {
         int off = this.off;
-        int minCapacity = off + 8;
-        if (minCapacity >= this.bytes.length) {
-            ensureCapacity(minCapacity);
+        byte[] bytes = this.bytes;
+        if (off + 8 > bytes.length) {
+            bytes = grow(off + 8);
         }
 
         UNSAFE.putLong(bytes, ARRAY_BYTE_BASE_OFFSET + off, name);
@@ -2403,9 +2455,9 @@ final class JSONWriterJSONB
     @Override
     public void writeName3Raw(long name) {
         int off = this.off;
-        int minCapacity = off + 8;
-        if (minCapacity >= this.bytes.length) {
-            ensureCapacity(minCapacity);
+        byte[] bytes = this.bytes;
+        if (off + 8 > bytes.length) {
+            bytes = grow(off + 8);
         }
 
         UNSAFE.putLong(bytes, ARRAY_BYTE_BASE_OFFSET + off, name);
@@ -2415,9 +2467,9 @@ final class JSONWriterJSONB
     @Override
     public void writeName4Raw(long name) {
         int off = this.off;
-        int minCapacity = off + 8;
-        if (minCapacity >= this.bytes.length) {
-            ensureCapacity(minCapacity);
+        byte[] bytes = this.bytes;
+        if (off + 8 > bytes.length) {
+            bytes = grow(off + 8);
         }
 
         UNSAFE.putLong(bytes, ARRAY_BYTE_BASE_OFFSET + off, name);
@@ -2427,9 +2479,9 @@ final class JSONWriterJSONB
     @Override
     public void writeName5Raw(long name) {
         int off = this.off;
-        int minCapacity = off + 8;
-        if (minCapacity >= this.bytes.length) {
-            ensureCapacity(minCapacity);
+        byte[] bytes = this.bytes;
+        if (off + 8 > bytes.length) {
+            bytes = grow(off + 8);
         }
 
         UNSAFE.putLong(bytes, ARRAY_BYTE_BASE_OFFSET + off, name);
@@ -2439,9 +2491,9 @@ final class JSONWriterJSONB
     @Override
     public void writeName6Raw(long name) {
         int off = this.off;
-        int minCapacity = off + 8;
-        if (minCapacity >= this.bytes.length) {
-            ensureCapacity(minCapacity);
+        byte[] bytes = this.bytes;
+        if (off + 8 > bytes.length) {
+            bytes = grow(off + 8);
         }
 
         UNSAFE.putLong(bytes, ARRAY_BYTE_BASE_OFFSET + off, name);
@@ -2451,9 +2503,9 @@ final class JSONWriterJSONB
     @Override
     public void writeName7Raw(long name) {
         int off = this.off;
-        int minCapacity = off + 8;
-        if (minCapacity >= this.bytes.length) {
-            ensureCapacity(minCapacity);
+        byte[] bytes = this.bytes;
+        if (off + 8 > bytes.length) {
+            bytes = grow(off + 8);
         }
 
         UNSAFE.putLong(bytes, ARRAY_BYTE_BASE_OFFSET + off, name);
@@ -2463,9 +2515,9 @@ final class JSONWriterJSONB
     @Override
     public void writeName8Raw(long name) {
         int off = this.off;
-        int minCapacity = off + 8;
-        if (minCapacity >= this.bytes.length) {
-            ensureCapacity(minCapacity);
+        byte[] bytes = this.bytes;
+        if (off + 8 > bytes.length) {
+            bytes = grow(off + 8);
         }
 
         UNSAFE.putLong(bytes, ARRAY_BYTE_BASE_OFFSET + off, name);
@@ -2475,9 +2527,9 @@ final class JSONWriterJSONB
     @Override
     public void writeName9Raw(long name0, int name1) {
         int off = this.off;
-        int minCapacity = off + 12;
-        if (minCapacity >= this.bytes.length) {
-            ensureCapacity(minCapacity);
+        byte[] bytes = this.bytes;
+        if (off + 12 > bytes.length) {
+            bytes = grow(off + 12);
         }
 
         UNSAFE.putLong(bytes, ARRAY_BYTE_BASE_OFFSET + off, name0);
@@ -2488,9 +2540,9 @@ final class JSONWriterJSONB
     @Override
     public void writeName10Raw(long name0, long name1) {
         int off = this.off;
-        int minCapacity = off + 16;
-        if (minCapacity >= this.bytes.length) {
-            ensureCapacity(minCapacity);
+        byte[] bytes = this.bytes;
+        if (off + 16 > bytes.length) {
+            bytes = grow(off + 16);
         }
 
         UNSAFE.putLong(bytes, ARRAY_BYTE_BASE_OFFSET + off, name0);
@@ -2501,9 +2553,9 @@ final class JSONWriterJSONB
     @Override
     public void writeName11Raw(long name0, long name1) {
         int off = this.off;
-        int minCapacity = off + 16;
-        if (minCapacity >= this.bytes.length) {
-            ensureCapacity(minCapacity);
+        byte[] bytes = this.bytes;
+        if (off + 16 > bytes.length) {
+            bytes = grow(off + 16);
         }
 
         UNSAFE.putLong(bytes, ARRAY_BYTE_BASE_OFFSET + off, name0);
@@ -2514,9 +2566,9 @@ final class JSONWriterJSONB
     @Override
     public void writeName12Raw(long name0, long name1) {
         int off = this.off;
-        int minCapacity = off + 16;
-        if (minCapacity >= this.bytes.length) {
-            ensureCapacity(minCapacity);
+        byte[] bytes = this.bytes;
+        if (off + 16 > bytes.length) {
+            bytes = grow(off + 16);
         }
 
         UNSAFE.putLong(bytes, ARRAY_BYTE_BASE_OFFSET + off, name0);
@@ -2527,9 +2579,9 @@ final class JSONWriterJSONB
     @Override
     public void writeName13Raw(long name0, long name1) {
         int off = this.off;
-        int minCapacity = off + 16;
-        if (minCapacity >= this.bytes.length) {
-            ensureCapacity(minCapacity);
+        byte[] bytes = this.bytes;
+        if (off + 16 > bytes.length) {
+            bytes = grow(off + 16);
         }
 
         UNSAFE.putLong(bytes, ARRAY_BYTE_BASE_OFFSET + off, name0);
@@ -2540,9 +2592,9 @@ final class JSONWriterJSONB
     @Override
     public void writeName14Raw(long name0, long name1) {
         int off = this.off;
-        int minCapacity = off + 16;
-        if (minCapacity >= this.bytes.length) {
-            ensureCapacity(minCapacity);
+        byte[] bytes = this.bytes;
+        if (off + 16 > bytes.length) {
+            bytes = grow(off + 16);
         }
 
         UNSAFE.putLong(bytes, ARRAY_BYTE_BASE_OFFSET + off, name0);
@@ -2553,9 +2605,9 @@ final class JSONWriterJSONB
     @Override
     public void writeName15Raw(long name0, long name1) {
         int off = this.off;
-        int minCapacity = off + 16;
-        if (minCapacity >= this.bytes.length) {
-            ensureCapacity(minCapacity);
+        byte[] bytes = this.bytes;
+        if (off + 16 > bytes.length) {
+            bytes = grow(off + 16);
         }
 
         UNSAFE.putLong(bytes, ARRAY_BYTE_BASE_OFFSET + off, name0);
@@ -2566,9 +2618,9 @@ final class JSONWriterJSONB
     @Override
     public void writeName16Raw(long name0, long name1) {
         int off = this.off;
-        int minCapacity = off + 16;
-        if (minCapacity >= this.bytes.length) {
-            ensureCapacity(minCapacity);
+        byte[] bytes = this.bytes;
+        if (off + 16 > bytes.length) {
+            bytes = grow(off + 16);
         }
 
         UNSAFE.putLong(bytes, ARRAY_BYTE_BASE_OFFSET + off, name0);
diff --git a/core/src/main/java/com/alibaba/fastjson2/JSONWriterUTF16.java b/core/src/main/java/com/alibaba/fastjson2/JSONWriterUTF16.java
index b850c217e..45a868560 100644
--- a/core/src/main/java/com/alibaba/fastjson2/JSONWriterUTF16.java
+++ b/core/src/main/java/com/alibaba/fastjson2/JSONWriterUTF16.java
@@ -64,13 +64,14 @@ class JSONWriterUTF16
     }
 
     public final void writeNull() {
+        int off = this.off;
         int minCapacity = off + 4;
-        if (minCapacity >= this.chars.length) {
-            ensureCapacity(minCapacity);
+        char[] chars = this.chars;
+        if (minCapacity > chars.length) {
+            chars = grow(minCapacity);
         }
-
         UNSAFE.putLong(chars, ARRAY_CHAR_BASE_OFFSET + ((long) off << 1), NULL_64);
-        off += 4;
+        this.off = off + 4;
     }
 
     @Override
@@ -99,8 +100,9 @@ class JSONWriterUTF16
     @Override
     protected final void write0(char c) {
         int off = this.off;
+        char[] chars = this.chars;
         if (off == chars.length) {
-            ensureCapacity(off + 1);
+            chars = grow(off + 1);
         }
         chars[off] = c;
         this.off = off + 1;
@@ -109,8 +111,9 @@ class JSONWriterUTF16
     @Override
     public final void writeColon() {
         int off = this.off;
+        char[] chars = this.chars;
         if (off == chars.length) {
-            ensureCapacity(off + 1);
+            chars = grow(off + 1);
         }
         chars[off] = ':';
         this.off = off + 1;
@@ -126,20 +129,16 @@ class JSONWriterUTF16
         startObject = true;
 
         int off = this.off;
-        int minCapacity = off + (pretty ? 3 + indent : 1);
-        if (minCapacity >= chars.length) {
-            ensureCapacity(minCapacity);
+        char[] chars = this.chars;
+        int minCapacity = off + 3 + pretty * level;
+        if (minCapacity > chars.length) {
+            chars = grow(minCapacity);
         }
 
-        final char[] chars = this.chars;
         chars[off++] = (byte) '{';
 
-        if (pretty) {
-            indent++;
-            chars[off++] = (byte) '\n';
-            for (int i = 0; i < indent; ++i) {
-                chars[off++] = (byte) '\t';
-            }
+        if (pretty != PRETTY_NON) {
+            off = indent(chars, off);
         }
         this.off = off;
     }
@@ -148,18 +147,14 @@ class JSONWriterUTF16
     public final void endObject() {
         level--;
         int off = this.off;
-        int minCapacity = off + (pretty ? 2 + indent : 1);
-        if (minCapacity >= chars.length) {
-            ensureCapacity(minCapacity);
+        int minCapacity = off + 1 + (pretty == 0 ? 0 : pretty * level + 1);
+        char[] chars = this.chars;
+        if (minCapacity > chars.length) {
+            chars = grow(minCapacity);
         }
 
-        final char[] chars = this.chars;
-        if (pretty) {
-            indent--;
-            chars[off++] = (byte) '\n';
-            for (int i = 0; i < indent; ++i) {
-                chars[off++] = (byte) '\t';
-            }
+        if (pretty != PRETTY_NON) {
+            off = indent(chars, off);
         }
 
         chars[off] = (byte) '}';
@@ -171,18 +166,15 @@ class JSONWriterUTF16
     public final void writeComma() {
         startObject = false;
         int off = this.off;
-        int minCapacity = off + (pretty ? 2 + indent : 1);
-        if (minCapacity >= chars.length) {
-            ensureCapacity(minCapacity);
+        int minCapacity = off + 2 + pretty * level;
+        char[] chars = this.chars;
+        if (minCapacity > chars.length) {
+            chars = grow(minCapacity);
         }
 
-        final char[] chars = this.chars;
         chars[off++] = (byte) ',';
-        if (pretty) {
-            chars[off++] = (byte) '\n';
-            for (int i = 0; i < indent; ++i) {
-                chars[off++] = (byte) '\t';
-            }
+        if (pretty != PRETTY_NON) {
+            off = indent(chars, off);
         }
         this.off = off;
     }
@@ -195,19 +187,15 @@ class JSONWriterUTF16
 
         level++;
         int off = this.off;
-        int minCapacity = off + (pretty ? 3 + indent : 1);
-        if (minCapacity >= chars.length) {
-            ensureCapacity(minCapacity);
+        int minCapacity = off + 3 + pretty * level;
+        char[] chars = this.chars;
+        if (minCapacity > chars.length) {
+            chars = grow(minCapacity);
         }
 
-        final char[] chars = this.chars;
         chars[off++] = (byte) '[';
-        if (pretty) {
-            indent++;
-            chars[off++] = (byte) '\n';
-            for (int i = 0; i < indent; ++i) {
-                chars[off++] = (byte) '\t';
-            }
+        if (pretty != PRETTY_NON) {
+            off = indent(chars, off);
         }
         this.off = off;
     }
@@ -216,18 +204,14 @@ class JSONWriterUTF16
     public final void endArray() {
         level--;
         int off = this.off;
-        int minCapacity = off + (pretty ? 2 + indent : 1);
-        if (minCapacity >= chars.length) {
-            ensureCapacity(minCapacity);
+        int minCapacity = off + 1 + (pretty == 0 ? 0 : pretty * level + 1);
+        char[] chars = this.chars;
+        if (minCapacity > chars.length) {
+            chars = grow(minCapacity);
         }
 
-        final char[] chars = this.chars;
-        if (pretty) {
-            indent--;
-            chars[off++] = (byte) '\n';
-            for (int i = 0; i < indent; ++i) {
-                chars[off++] = (byte) '\t';
-            }
+        if (pretty != PRETTY_NON) {
+            off = indent(chars, off);
         }
         chars[off] = (byte) ']';
         this.off = off + 1;
@@ -235,16 +219,20 @@ class JSONWriterUTF16
     }
 
     public final void writeString(List<String> list) {
+        if (pretty != PRETTY_NON) {
+            super.writeString(list);
+            return;
+        }
         // startArray();
         if (off == chars.length) {
-            ensureCapacity(off + 1);
+            grow(off + 1);
         }
         chars[off++] = '[';
 
         for (int i = 0, size = list.size(); i < size; i++) {
             if (i != 0) {
                 if (off == chars.length) {
-                    ensureCapacity(off + 1);
+                    grow(off + 1);
                 }
                 chars[off++] = ',';
             }
@@ -254,7 +242,7 @@ class JSONWriterUTF16
         }
 
         if (off == chars.length) {
-            ensureCapacity(off + 1);
+            grow(off + 1);
         }
         chars[off++] = ']';
     }
@@ -269,7 +257,7 @@ class JSONWriterUTF16
         int off = this.off;
         int minCapacity = off + value.length + 2;
         if (minCapacity >= chars.length) {
-            ensureCapacity(minCapacity);
+            grow(minCapacity);
         }
 
         final int start = off;
@@ -300,7 +288,7 @@ class JSONWriterUTF16
         int off = this.off;
         int minCapacity = off + value.length + 2;
         if (minCapacity >= chars.length) {
-            ensureCapacity(minCapacity);
+            grow(minCapacity);
         }
 
         final int start = off;
@@ -339,7 +327,7 @@ class JSONWriterUTF16
         int off = this.off;
         int minCapacity = off + value.length + 2;
         if (minCapacity >= chars.length) {
-            ensureCapacity(minCapacity);
+            grow(minCapacity);
         }
 
         final char[] chars = this.chars;
@@ -384,7 +372,7 @@ class JSONWriterUTF16
         final int strlen = str.length();
         int minCapacity = off + strlen + 2;
         if (minCapacity >= chars.length) {
-            ensureCapacity(minCapacity);
+            grow(minCapacity);
         }
 
         for (int i = 0; i < strlen; i++) {
@@ -1008,11 +996,11 @@ class JSONWriterUTF16
             minCapacity += len * 2;
         }
 
+        char[] chars = this.chars;
         if (minCapacity - chars.length > 0) {
-            ensureCapacity(minCapacity);
+            chars = grow(minCapacity);
         }
 
-        final char[] chars = this.chars;
         if (quoted) {
             chars[off++] = quote;
         }
@@ -1171,8 +1159,9 @@ class JSONWriterUTF16
         writeRaw(REF_PREF, 0, REF_PREF.length);
         writeString(path);
         int off = this.off;
+        char[] chars = this.chars;
         if (off == chars.length) {
-            ensureCapacity(off + 1);
+            chars = grow(off + 1);
         }
         chars[off] = '}';
         this.off = off + 1;
@@ -1276,18 +1265,18 @@ class JSONWriterUTF16
         int strlen = str.length();
 
         int minCapacity = off + strlen + (writeAsString ? 2 : 0);
-        if (minCapacity >= this.chars.length) {
-            ensureCapacity(minCapacity);
+        char[] chars = this.chars;
+        if (minCapacity > this.chars.length) {
+            chars = grow(minCapacity);
         }
 
-        final char[] bytes = this.chars;
         if (writeAsString) {
-            bytes[off++] = '"';
+            chars[off++] = '"';
         }
-        str.getChars(0, strlen, bytes, off);
+        str.getChars(0, strlen, chars, off);
         off += strlen;
         if (writeAsString) {
-            bytes[off++] = '"';
+            chars[off++] = '"';
         }
         this.off = off;
     }
@@ -1311,11 +1300,11 @@ class JSONWriterUTF16
 
         int off = this.off;
         int minCapacity = off + precision + value.scale() + 7;
-        if (minCapacity >= chars.length) {
-            ensureCapacity(minCapacity);
+        char[] chars = this.chars;
+        if (minCapacity > chars.length) {
+            chars = grow(minCapacity);
         }
 
-        final char[] chars = this.chars;
         if (writeAsString) {
             chars[off++] = '"';
         }
@@ -1362,11 +1351,11 @@ class JSONWriterUTF16
         long lsb = value.getLeastSignificantBits();
 
         int minCapacity = off + 38;
-        if (minCapacity >= chars.length) {
-            ensureCapacity(minCapacity);
+        char[] buf = this.chars;
+        if (minCapacity > chars.length) {
+            buf = grow(minCapacity);
         }
 
-        final char[] buf = this.chars;
         final int off = this.off;
         buf[off] = '"';
         putLong(buf, off + 1, (int) (msb >> 56), (int) (msb >> 48));
@@ -1387,33 +1376,35 @@ class JSONWriterUTF16
 
     @Override
     public final void writeRaw(String str) {
-        ensureCapacity(off + str.length());
-        str.getChars(0, str.length(), chars, off);
-        off += str.length();
+        int strlen = str.length();
+        int off = this.off;
+        char[] chars = this.chars;
+        if (off + strlen > chars.length) {
+            chars = grow(off + strlen);
+        }
+        str.getChars(0, strlen, chars, off);
+        this.off = off + strlen;
     }
 
     @Override
-    public final void writeRaw(char[] chars, int off, int charslen) {
-        {
-            // inline ensureCapacity
-            int minCapacity = this.off + charslen;
-            if (minCapacity >= this.chars.length) {
-                ensureCapacity(minCapacity);
-            }
+    public final void writeRaw(char[] str, int coff, int strlen) {
+        int off = this.off;
+        char[] chars = this.chars;
+        if (off + strlen > chars.length) {
+            chars = grow(off + strlen);
         }
-        System.arraycopy(chars, off, this.chars, this.off, charslen);
-        this.off += charslen;
+        System.arraycopy(str, coff, chars, off, strlen);
+        this.off = off + strlen;
     }
 
     @Override
     public final void writeChar(char ch) {
         int off = this.off;
-        int minCapacity = off + 8;
-        if (minCapacity >= chars.length) {
-            ensureCapacity(minCapacity);
+        char[] chars = this.chars;
+        if (off + 8 > chars.length) {
+            chars = grow(off + 8);
         }
 
-        final char[] chars = this.chars;
         chars[off++] = quote;
         switch (ch) {
             case '"':
@@ -1531,8 +1522,9 @@ class JSONWriterUTF16
     @Override
     public final void writeRaw(char c0, char c1) {
         int off = this.off;
-        if (off + 1 >= chars.length) {
-            ensureCapacity(off + 2);
+        char[] chars = this.chars;
+        if (off + 2 > chars.length) {
+            chars = grow(off + 2);
         }
         chars[off] = c0;
         chars[off + 1] = c1;
@@ -1542,18 +1534,18 @@ class JSONWriterUTF16
     @Override
     public final void writeNameRaw(char[] name) {
         int off = this.off;
-        int minCapacity = off + name.length + 2 + indent;
-        if (minCapacity >= this.chars.length) {
-            ensureCapacity(minCapacity);
+        int minCapacity = off + name.length + 2 + pretty * level;
+        char[] chars = this.chars;
+        if (minCapacity > chars.length) {
+            chars = grow(minCapacity);
         }
 
         if (startObject) {
             startObject = false;
         } else {
-            final char[] chars = this.chars;
             chars[off++] = ',';
-            if (pretty) {
-                off = indent(chars, off, indent);
+            if (pretty != PRETTY_NON) {
+                off = indent(chars, off);
             }
         }
         System.arraycopy(name, 0, chars, off, name.length);
@@ -1563,18 +1555,18 @@ class JSONWriterUTF16
     @Override
     public final void writeName2Raw(long name) {
         int off = this.off;
-        int minCapacity = off + 10 + indent;
-        if (minCapacity >= this.chars.length) {
-            ensureCapacity(minCapacity);
+        int minCapacity = off + 10 + pretty * level;
+        char[] chars = this.chars;
+        if (minCapacity > chars.length) {
+            chars = grow(minCapacity);
         }
 
-        char[] chars = this.chars;
         if (startObject) {
             startObject = false;
         } else {
             chars[off++] = ',';
-            if (pretty) {
-                off = indent(chars, off, indent);
+            if (pretty != PRETTY_NON) {
+                off = indent(chars, off);
             }
         }
 
@@ -1585,18 +1577,18 @@ class JSONWriterUTF16
     @Override
     public final void writeName3Raw(long name) {
         int off = this.off;
-        int minCapacity = off + 10 + indent;
-        if (minCapacity >= this.chars.length) {
-            ensureCapacity(minCapacity);
+        int minCapacity = off + 10 + pretty * level;
+        char[] chars = this.chars;
+        if (minCapacity > chars.length) {
+            chars = grow(minCapacity);
         }
 
-        char[] chars = this.chars;
         if (startObject) {
             startObject = false;
         } else {
             chars[off++] = ',';
-            if (pretty) {
-                off = indent(chars, off, indent);
+            if (pretty != PRETTY_NON) {
+                off = indent(chars, off);
             }
         }
 
@@ -1607,18 +1599,18 @@ class JSONWriterUTF16
     @Override
     public final void writeName4Raw(long name) {
         int off = this.off;
-        int minCapacity = off + 10 + indent;
-        if (minCapacity >= this.chars.length) {
-            ensureCapacity(minCapacity);
+        int minCapacity = off + 10 + pretty * level;
+        char[] chars = this.chars;
+        if (minCapacity > chars.length) {
+            chars = grow(minCapacity);
         }
 
-        char[] chars = this.chars;
         if (startObject) {
             startObject = false;
         } else {
             chars[off++] = ',';
-            if (pretty) {
-                off = indent(chars, off, indent);
+            if (pretty != PRETTY_NON) {
+                off = indent(chars, off);
             }
         }
 
@@ -1629,18 +1621,18 @@ class JSONWriterUTF16
     @Override
     public final void writeName5Raw(long name) {
         int off = this.off;
-        int minCapacity = off + 10 + indent;
-        if (minCapacity >= this.chars.length) {
-            ensureCapacity(minCapacity);
+        int minCapacity = off + 10 + pretty * level;
+        char[] chars = this.chars;
+        if (minCapacity > chars.length) {
+            chars = grow(minCapacity);
         }
 
-        char[] chars = this.chars;
         if (startObject) {
             startObject = false;
         } else {
             chars[off++] = ',';
-            if (pretty) {
-                off = indent(chars, off, indent);
+            if (pretty != PRETTY_NON) {
+                off = indent(chars, off);
             }
         }
 
@@ -1651,18 +1643,18 @@ class JSONWriterUTF16
     @Override
     public final void writeName6Raw(long name) {
         int off = this.off;
-        int minCapacity = off + 11 + indent;
-        if (minCapacity >= this.chars.length) {
-            ensureCapacity(minCapacity);
+        int minCapacity = off + 11 + pretty * level;
+        char[] chars = this.chars;
+        if (minCapacity > chars.length) {
+            chars = grow(minCapacity);
         }
 
-        char[] chars = this.chars;
         if (startObject) {
             startObject = false;
         } else {
             chars[off++] = ',';
-            if (pretty) {
-                off = indent(chars, off, indent);
+            if (pretty != PRETTY_NON) {
+                off = indent(chars, off);
             }
         }
 
@@ -1674,18 +1666,18 @@ class JSONWriterUTF16
     @Override
     public final void writeName7Raw(long name) {
         int off = this.off;
-        int minCapacity = off + 12 + indent;
-        if (minCapacity >= this.chars.length) {
-            ensureCapacity(minCapacity);
+        int minCapacity = off + 12 + pretty * level;
+        char[] chars = this.chars;
+        if (minCapacity > chars.length) {
+            chars = grow(minCapacity);
         }
 
-        char[] chars = this.chars;
         if (startObject) {
             startObject = false;
         } else {
             chars[off++] = ',';
-            if (pretty) {
-                off = indent(chars, off, indent);
+            if (pretty != PRETTY_NON) {
+                off = indent(chars, off);
             }
         }
 
@@ -1698,18 +1690,18 @@ class JSONWriterUTF16
     @Override
     public final void writeName8Raw(long name) {
         int off = this.off;
-        int minCapacity = off + 13 + indent;
-        if (minCapacity >= this.chars.length) {
-            ensureCapacity(minCapacity);
+        int minCapacity = off + 13 + pretty * level;
+        char[] chars = this.chars;
+        if (minCapacity > chars.length) {
+            chars = grow(minCapacity);
         }
 
-        char[] chars = this.chars;
         if (startObject) {
             startObject = false;
         } else {
             chars[off++] = ',';
-            if (pretty) {
-                off = indent(chars, off, indent);
+            if (pretty != PRETTY_NON) {
+                off = indent(chars, off);
             }
         }
 
@@ -1723,18 +1715,18 @@ class JSONWriterUTF16
     @Override
     public final void writeName9Raw(long name0, int name1) {
         int off = this.off;
-        int minCapacity = off + 14 + indent;
-        if (minCapacity >= this.chars.length) {
-            ensureCapacity(minCapacity);
+        int minCapacity = off + 14 + pretty * level;
+        char[] chars = this.chars;
+        if (minCapacity > chars.length) {
+            chars = grow(minCapacity);
         }
 
-        final char[] chars = this.chars;
         if (startObject) {
             startObject = false;
         } else {
             chars[off++] = ',';
-            if (pretty) {
-                off = indent(chars, off, indent);
+            if (pretty != PRETTY_NON) {
+                off = indent(chars, off);
             }
         }
 
@@ -1745,18 +1737,18 @@ class JSONWriterUTF16
     @Override
     public final void writeName10Raw(long name0, long name1) {
         int off = this.off;
-        int minCapacity = off + 18 + indent;
-        if (minCapacity >= this.chars.length) {
-            ensureCapacity(minCapacity);
+        int minCapacity = off + 18 + pretty * level;
+        char[] chars = this.chars;
+        if (minCapacity > chars.length) {
+            chars = grow(minCapacity);
         }
 
-        final char[] chars = this.chars;
         if (startObject) {
             startObject = false;
         } else {
             chars[off++] = ',';
-            if (pretty) {
-                off = indent(chars, off, indent);
+            if (pretty != PRETTY_NON) {
+                off = indent(chars, off);
             }
         }
 
@@ -1767,18 +1759,18 @@ class JSONWriterUTF16
     @Override
     public final void writeName11Raw(long name0, long name1) {
         int off = this.off;
-        int minCapacity = off + 18 + indent;
-        if (minCapacity >= this.chars.length) {
-            ensureCapacity(minCapacity);
+        int minCapacity = off + 18 + pretty * level;
+        char[] chars = this.chars;
+        if (minCapacity > chars.length) {
+            chars = grow(minCapacity);
         }
 
-        final char[] chars = this.chars;
         if (startObject) {
             startObject = false;
         } else {
             chars[off++] = ',';
-            if (pretty) {
-                off = indent(chars, off, indent);
+            if (pretty != PRETTY_NON) {
+                off = indent(chars, off);
             }
         }
 
@@ -1789,18 +1781,18 @@ class JSONWriterUTF16
     @Override
     public final void writeName12Raw(long name0, long name1) {
         int off = this.off;
-        int minCapacity = off + 18 + indent;
-        if (minCapacity >= this.chars.length) {
-            ensureCapacity(minCapacity);
+        int minCapacity = off + 18 + pretty * level;
+        char[] chars = this.chars;
+        if (minCapacity > chars.length) {
+            chars = grow(minCapacity);
         }
 
-        final char[] chars = this.chars;
         if (startObject) {
             startObject = false;
         } else {
             chars[off++] = ',';
-            if (pretty) {
-                off = indent(chars, off, indent);
+            if (pretty != PRETTY_NON) {
+                off = indent(chars, off);
             }
         }
 
@@ -1811,18 +1803,18 @@ class JSONWriterUTF16
     @Override
     public final void writeName13Raw(long name0, long name1) {
         int off = this.off;
-        int minCapacity = off + 18 + indent;
-        if (minCapacity >= this.chars.length) {
-            ensureCapacity(minCapacity);
+        int minCapacity = off + 18 + pretty * level;
+        char[] chars = this.chars;
+        if (minCapacity > chars.length) {
+            chars = grow(minCapacity);
         }
 
-        final char[] chars = this.chars;
         if (startObject) {
             startObject = false;
         } else {
             chars[off++] = ',';
-            if (pretty) {
-                off = indent(chars, off, indent);
+            if (pretty != PRETTY_NON) {
+                off = indent(chars, off);
             }
         }
 
@@ -1833,18 +1825,18 @@ class JSONWriterUTF16
     @Override
     public final void writeName14Raw(long name0, long name1) {
         int off = this.off;
-        int minCapacity = off + 19 + indent;
-        if (minCapacity >= this.chars.length) {
-            ensureCapacity(minCapacity);
+        int minCapacity = off + 19 + pretty * level;
+        char[] chars = this.chars;
+        if (minCapacity > chars.length) {
+            chars = grow(minCapacity);
         }
 
-        final char[] chars = this.chars;
         if (startObject) {
             startObject = false;
         } else {
             chars[off++] = ',';
-            if (pretty) {
-                off = indent(chars, off, indent);
+            if (pretty != PRETTY_NON) {
+                off = indent(chars, off);
             }
         }
 
@@ -1856,18 +1848,18 @@ class JSONWriterUTF16
     @Override
     public final void writeName15Raw(long name0, long name1) {
         int off = this.off;
-        int minCapacity = off + 20 + indent;
-        if (minCapacity >= this.chars.length) {
-            ensureCapacity(minCapacity);
+        int minCapacity = off + 20 + pretty * level;
+        char[] chars = this.chars;
+        if (minCapacity > chars.length) {
+            chars = grow(minCapacity);
         }
 
-        final char[] chars = this.chars;
         if (startObject) {
             startObject = false;
         } else {
             chars[off++] = ',';
-            if (pretty) {
-                off = indent(chars, off, indent);
+            if (pretty != PRETTY_NON) {
+                off = indent(chars, off);
             }
         }
 
@@ -1880,18 +1872,18 @@ class JSONWriterUTF16
     @Override
     public final void writeName16Raw(long name0, long name1) {
         int off = this.off;
-        int minCapacity = off + 21 + indent;
-        if (minCapacity >= this.chars.length) {
-            ensureCapacity(minCapacity);
+        int minCapacity = off + 21 + pretty * level;
+        char[] chars = this.chars;
+        if (minCapacity > chars.length) {
+            chars = grow(minCapacity);
         }
 
-        final char[] chars = this.chars;
         if (startObject) {
             startObject = false;
         } else {
             chars[off++] = ',';
-            if (pretty) {
-                off = indent(chars, off, indent);
+            if (pretty != PRETTY_NON) {
+                off = indent(chars, off);
             }
         }
 
@@ -1962,45 +1954,49 @@ class JSONWriterUTF16
                         | ((name1 & 0xFF00_0000_0000_0000L) >> 8));
     }
 
-    private static int indent(char[] chars, int off, int indent) {
-        chars[off++] = '\n';
-        int end = off + indent;
-        while (off < end) {
-            chars[off++] = '\t';
-        }
-        return off;
+    private int indent(char[] chars, int off) {
+        chars[off] = '\n';
+        int toIndex = off + 1 + pretty * level;
+        Arrays.fill(chars, off + 1, toIndex, pretty == PRETTY_TAB ? '\t' : ' ');
+        return toIndex;
     }
 
     @Override
-    public final void writeNameRaw(char[] chars, int off, int len) {
-        int minCapacity = this.off + len + 2 + indent;
-        if (minCapacity >= this.chars.length) {
-            ensureCapacity(minCapacity);
+    public final void writeNameRaw(char[] name, int coff, int len) {
+        int off = this.off;
+        int minCapacity = off + len + 2 + pretty * level;
+        char[] chars = this.chars;
+        if (minCapacity > chars.length) {
+            chars = grow(minCapacity);
         }
 
         if (startObject) {
             startObject = false;
         } else {
-            this.chars[this.off++] = ',';
+            chars[off++] = ',';
         }
-        System.arraycopy(chars, off, this.chars, this.off, len);
-        this.off += len;
+        System.arraycopy(name, coff, chars, off, len);
+        this.off = off + len;
     }
 
     final void ensureCapacity(int minCapacity) {
-        if (minCapacity - chars.length > 0) {
-            int oldCapacity = chars.length;
-            int newCapacity = oldCapacity + (oldCapacity >> 1);
-            if (newCapacity - minCapacity < 0) {
-                newCapacity = minCapacity;
-            }
-            if (newCapacity - maxArraySize > 0) {
-                throw new OutOfMemoryError("try enabling LargeObject feature instead");
-            }
+        if (minCapacity > chars.length) {
+            grow(minCapacity);
+        }
+    }
 
-            // minCapacity is usually close to size, so this is a win:
-            chars = Arrays.copyOf(chars, newCapacity);
+    private char[] grow(int minCapacity) {
+        int oldCapacity = chars.length;
+        int newCapacity = oldCapacity + (oldCapacity >> 1);
+        if (newCapacity - minCapacity < 0) {
+            newCapacity = minCapacity;
+        }
+        if (newCapacity - maxArraySize > 0) {
+            throw new OutOfMemoryError("try enabling LargeObject feature instead");
         }
+
+        // minCapacity is usually close to size, so this is a win:
+        return chars = Arrays.copyOf(chars, newCapacity);
     }
 
     public final void writeInt32(int[] value) {
@@ -2013,11 +2009,11 @@ class JSONWriterUTF16
 
         int off = this.off;
         int minCapacity = off + value.length * 13 + 2;
-        if (minCapacity >= chars.length) {
-            ensureCapacity(minCapacity);
+        char[] chars = this.chars;
+        if (minCapacity > chars.length) {
+            chars = grow(minCapacity);
         }
 
-        final char[] chars = this.chars;
         chars[off++] = '[';
 
         for (int i = 0; i < value.length; i++) {
@@ -2043,11 +2039,11 @@ class JSONWriterUTF16
 
         int off = this.off;
         int minCapacity = off + 7;
-        if (minCapacity >= chars.length) {
-            ensureCapacity(minCapacity);
+        char[] chars = this.chars;
+        if (minCapacity > chars.length) {
+            chars = grow(minCapacity);
         }
 
-        final char[] chars = this.chars;
         if (writeAsString) {
             chars[off++] = quote;
         }
@@ -2068,11 +2064,11 @@ class JSONWriterUTF16
 
         int off = this.off;
         int minCapacity = off + value.length * 5 + 2;
-        if (minCapacity >= chars.length) {
-            ensureCapacity(minCapacity);
+        char[] chars = this.chars;
+        if (minCapacity > chars.length) {
+            chars = grow(minCapacity);
         }
 
-        final char[] chars = this.chars;
         chars[off++] = '[';
 
         for (int i = 0; i < value.length; i++) {
@@ -2098,11 +2094,11 @@ class JSONWriterUTF16
 
         int off = this.off;
         int minCapacity = off + 7;
-        if (minCapacity >= chars.length) {
-            ensureCapacity(minCapacity);
+        char[] chars = this.chars;
+        if (minCapacity > chars.length) {
+            chars = grow(minCapacity);
         }
 
-        final char[] chars = this.chars;
         if (writeAsString) {
             chars[off++] = quote;
         }
@@ -2119,11 +2115,11 @@ class JSONWriterUTF16
 
         int off = this.off;
         int minCapacity = off + 13;
-        if (minCapacity >= chars.length) {
-            ensureCapacity(minCapacity);
+        char[] chars = this.chars;
+        if (minCapacity > chars.length) {
+            chars = grow(minCapacity);
         }
 
-        final char[] chars = this.chars;
         if (writeAsString) {
             chars[off++] = quote;
         }
@@ -2152,11 +2148,11 @@ class JSONWriterUTF16
 
         int off = this.off;
         int minCapacity = off + 2 + values.length * 23;
-        if (minCapacity >= chars.length) {
-            ensureCapacity(minCapacity);
+        char[] chars = this.chars;
+        if (minCapacity > chars.length) {
+            chars = grow(minCapacity);
         }
 
-        final char[] chars = this.chars;
         chars[off++] = '[';
 
         for (int i = 0; i < values.length; i++) {
@@ -2234,11 +2230,11 @@ class JSONWriterUTF16
         int size = values.size();
         int off = this.off;
         int minCapacity = off + 2 + size * 23;
-        if (minCapacity >= chars.length) {
-            ensureCapacity(minCapacity);
+        char[] chars = this.chars;
+        if (minCapacity > chars.length) {
+            chars = grow(minCapacity);
         }
 
-        final char[] chars = this.chars;
         chars[off++] = (byte) '[';
 
         for (int i = 0; i < size; i++) {
@@ -2276,11 +2272,11 @@ class JSONWriterUTF16
         boolean writeAsString = isWriteAsString(i, features);
         int off = this.off;
         int minCapacity = off + 23;
-        if (minCapacity >= chars.length) {
-            ensureCapacity(minCapacity);
+        char[] chars = this.chars;
+        if (minCapacity > chars.length) {
+            chars = grow(minCapacity);
         }
 
-        final char[] chars = this.chars;
         if (writeAsString) {
             chars[off++] = quote;
         }
@@ -2315,9 +2311,11 @@ class JSONWriterUTF16
             minCapacity += 2;
         }
 
-        ensureCapacity(minCapacity);
+        char[] chars = this.chars;
+        if (minCapacity > chars.length) {
+            chars = grow(minCapacity);
+        }
 
-        final char[] chars = this.chars;
         if (writeAsString) {
             chars[off++] = '"';
         }
@@ -2342,11 +2340,11 @@ class JSONWriterUTF16
 
         int off = this.off;
         int minCapacity = off + values.length * (writeAsString ? 16 : 18) + 1;
-        if (minCapacity >= chars.length) {
-            ensureCapacity(minCapacity);
+        char[] chars = this.chars;
+        if (minCapacity > chars.length) {
+            chars = grow(minCapacity);
         }
 
-        final char[] chars = this.chars;
         chars[off++] = '[';
         for (int i = 0; i < values.length; i++) {
             if (i != 0) {
@@ -2379,11 +2377,11 @@ class JSONWriterUTF16
             minCapacity += 2;
         }
 
-        if (minCapacity >= chars.length) {
-            ensureCapacity(minCapacity);
+        char[] chars = this.chars;
+        if (minCapacity > chars.length) {
+            chars = grow(minCapacity);
         }
 
-        final char[] chars = this.chars;
         if (writeAsString) {
             chars[off++] = '"';
         }
@@ -2406,9 +2404,11 @@ class JSONWriterUTF16
             minCapacity += 2;
         }
 
-        ensureCapacity(minCapacity);
+        char[] chars = this.chars;
+        if (minCapacity > chars.length) {
+            chars = grow(minCapacity);
+        }
 
-        final char[] chars = this.chars;
         chars[off++] = '[';
 
         if (writeAsString) {
@@ -2446,11 +2446,11 @@ class JSONWriterUTF16
 
         int off = this.off;
         int minCapacity = off + values.length * 27 + 1;
-        if (minCapacity >= chars.length) {
-            ensureCapacity(minCapacity);
+        char[] chars = this.chars;
+        if (minCapacity > chars.length) {
+            chars = grow(minCapacity);
         }
 
-        final char[] chars = this.chars;
         chars[off++] = '[';
         for (int i = 0; i < values.length; i++) {
             if (i != 0) {
@@ -2484,12 +2484,12 @@ class JSONWriterUTF16
     ) {
         int off = this.off;
         int minCapacity = off + 16;
-        if (minCapacity >= this.chars.length) {
-            ensureCapacity(minCapacity);
+        char[] chars = this.chars;
+        if (minCapacity > chars.length) {
+            chars = grow(minCapacity);
         }
 
-        final char[] bytes = this.chars;
-        bytes[off] = quote;
+        chars[off] = quote;
         if (year < 0 || year > 9999) {
             throw illegalYear(year);
         }
@@ -2497,12 +2497,12 @@ class JSONWriterUTF16
         int y23 = year - y01 * 100;
         UNSAFE.putInt(chars, ARRAY_CHAR_BASE_OFFSET + ((long) (off + 1) << 1), PACKED_DIGITS_UTF16[y01]);
         UNSAFE.putInt(chars, ARRAY_CHAR_BASE_OFFSET + ((long) (off + 3) << 1), PACKED_DIGITS_UTF16[y23]);
-        UNSAFE.putInt(bytes, ARRAY_CHAR_BASE_OFFSET + ((long) (off + 5) << 1), PACKED_DIGITS_UTF16[month]);
-        UNSAFE.putInt(bytes, ARRAY_CHAR_BASE_OFFSET + ((long) (off + 7) << 1), PACKED_DIGITS_UTF16[dayOfMonth]);
-        UNSAFE.putInt(bytes, ARRAY_CHAR_BASE_OFFSET + ((long) (off + 9) << 1), PACKED_DIGITS_UTF16[hour]);
-        UNSAFE.putInt(bytes, ARRAY_CHAR_BASE_OFFSET + ((long) (off + 11) << 1), PACKED_DIGITS_UTF16[minute]);
-        UNSAFE.putInt(bytes, ARRAY_CHAR_BASE_OFFSET + ((long) (off + 13) << 1), PACKED_DIGITS_UTF16[second]);
-        bytes[off + 15] = quote;
+        UNSAFE.putInt(chars, ARRAY_CHAR_BASE_OFFSET + ((long) (off + 5) << 1), PACKED_DIGITS_UTF16[month]);
+        UNSAFE.putInt(chars, ARRAY_CHAR_BASE_OFFSET + ((long) (off + 7) << 1), PACKED_DIGITS_UTF16[dayOfMonth]);
+        UNSAFE.putInt(chars, ARRAY_CHAR_BASE_OFFSET + ((long) (off + 9) << 1), PACKED_DIGITS_UTF16[hour]);
+        UNSAFE.putInt(chars, ARRAY_CHAR_BASE_OFFSET + ((long) (off + 11) << 1), PACKED_DIGITS_UTF16[minute]);
+        UNSAFE.putInt(chars, ARRAY_CHAR_BASE_OFFSET + ((long) (off + 13) << 1), PACKED_DIGITS_UTF16[second]);
+        chars[off + 15] = quote;
         this.off = off + 16;
     }
 
@@ -2514,9 +2514,11 @@ class JSONWriterUTF16
             int hour,
             int minute,
             int second) {
-        ensureCapacity(off + 21);
+        char[] chars = this.chars;
+        if (off + 21 > chars.length) {
+            chars = grow(off + 21);
+        }
 
-        final char[] chars = this.chars;
         int off = this.off;
         chars[off] = quote;
         if (year < 0 || year > 9999) {
@@ -2543,10 +2545,10 @@ class JSONWriterUTF16
 
         int off = this.off;
         int minCapacity = off + 18;
-        if (minCapacity >= chars.length) {
-            ensureCapacity(minCapacity);
+        char[] chars = this.chars;
+        if (minCapacity > chars.length) {
+            chars = grow(minCapacity);
         }
-        final char[] chars = this.chars;
         chars[off++] = quote;
         off = IOUtils.writeLocalDate(chars, off, date.getYear(), date.getMonthValue(), date.getDayOfMonth());
         chars[off] = quote;
@@ -2557,11 +2559,10 @@ class JSONWriterUTF16
     public final void writeLocalDateTime(LocalDateTime dateTime) {
         int off = this.off;
         int minCapacity = off + 38;
-        if (minCapacity >= chars.length) {
-            ensureCapacity(minCapacity);
+        char[] chars = this.chars;
+        if (minCapacity > chars.length) {
+            chars = grow(minCapacity);
         }
-
-        final char[] chars = this.chars;
         chars[off++] = quote;
         LocalDate localDate = dateTime.toLocalDate();
         off = IOUtils.writeLocalDate(chars, off, localDate.getYear(), localDate.getMonthValue(), localDate.getDayOfMonth());
@@ -2592,11 +2593,11 @@ class JSONWriterUTF16
 
         int off = this.off;
         int minCapacity = off + 25 + zonelen;
-        if (off + minCapacity >= chars.length) {
-            ensureCapacity(minCapacity);
+        char[] chars = this.chars;
+        if (minCapacity > chars.length) {
+            chars = grow(minCapacity);
         }
 
-        final char[] chars = this.chars;
         chars[off] = quote;
         off = IOUtils.writeLocalDate(chars, off + 1, year, month, dayOfMonth);
         chars[off] = timeZone ? 'T' : ' ';
@@ -2615,7 +2616,7 @@ class JSONWriterUTF16
                 chars[off++] = '.';
                 final int rem2 = div - div2 * 10;
                 if (rem2 != 0) {
-                    UNSAFE.putInt(this.chars, ARRAY_CHAR_BASE_OFFSET + ((long) off << 1), PACKED_DIGITS_UTF16[div]);
+                    UNSAFE.putInt(chars, ARRAY_CHAR_BASE_OFFSET + ((long) off << 1), PACKED_DIGITS_UTF16[div]);
                     off += 2;
                 } else {
                     chars[off++] = (char) (byte) (div2 + '0');
@@ -2630,13 +2631,13 @@ class JSONWriterUTF16
             } else {
                 int offsetAbs = Math.abs(offset);
                 chars[off] = offset >= 0 ? '+' : '-';
-                UNSAFE.putInt(this.chars, ARRAY_CHAR_BASE_OFFSET + ((long) (off + 1) << 1), PACKED_DIGITS_UTF16[offsetAbs]);
+                UNSAFE.putInt(chars, ARRAY_CHAR_BASE_OFFSET + ((long) (off + 1) << 1), PACKED_DIGITS_UTF16[offsetAbs]);
                 chars[off + 3] = ':';
                 int offsetMinutes = (offsetSeconds - offset * 3600) / 60;
                 if (offsetMinutes < 0) {
                     offsetMinutes = -offsetMinutes;
                 }
-                UNSAFE.putInt(this.chars, ARRAY_CHAR_BASE_OFFSET + ((long) (off + 4) << 1), PACKED_DIGITS_UTF16[offsetMinutes]);
+                UNSAFE.putInt(chars, ARRAY_CHAR_BASE_OFFSET + ((long) (off + 4) << 1), PACKED_DIGITS_UTF16[offsetMinutes]);
                 off += 6;
             }
         }
@@ -2648,11 +2649,10 @@ class JSONWriterUTF16
     public final void writeDateYYYMMDD8(int year, int month, int dayOfMonth) {
         int off = this.off;
         int minCapacity = off + 10;
-        if (minCapacity >= this.chars.length) {
-            ensureCapacity(minCapacity);
+        char[] chars = this.chars;
+        if (minCapacity > chars.length) {
+            chars = grow(minCapacity);
         }
-
-        final char[] chars = this.chars;
         chars[off] = quote;
         if (year < 0 || year > 9999) {
             throw illegalYear(year);
@@ -2671,11 +2671,10 @@ class JSONWriterUTF16
     public final void writeDateYYYMMDD10(int year, int month, int dayOfMonth) {
         int off = this.off;
         int minCapacity = off + 13;
-        if (minCapacity >= chars.length) {
-            ensureCapacity(minCapacity);
+        char[] chars = this.chars;
+        if (minCapacity > chars.length) {
+            chars = grow(minCapacity);
         }
-
-        final char[] chars = this.chars;
         chars[off++] = quote;
         off = IOUtils.writeLocalDate(chars, off, year, month, dayOfMonth);
         chars[off] = quote;
@@ -2686,11 +2685,10 @@ class JSONWriterUTF16
     public final void writeTimeHHMMSS8(int hour, int minute, int second) {
         int off = this.off;
         int minCapacity = off + 10;
-        if (minCapacity >= this.chars.length) {
-            ensureCapacity(minCapacity);
+        char[] chars = this.chars;
+        if (minCapacity > chars.length) {
+            chars = grow(minCapacity);
         }
-
-        final char[] chars = this.chars;
         chars[off] = (char) (byte) quote;
         UNSAFE.putInt(chars, ARRAY_CHAR_BASE_OFFSET + ((long) (off + 1) << 1), PACKED_DIGITS_UTF16[hour]);
         chars[off + 3] = ':';
@@ -2705,11 +2703,10 @@ class JSONWriterUTF16
     public final void writeLocalTime(LocalTime time) {
         int off = this.off;
         int minCapacity = off + 20;
-        if (minCapacity >= chars.length) {
-            ensureCapacity(minCapacity);
+        char[] chars = this.chars;
+        if (minCapacity > chars.length) {
+            chars = grow(minCapacity);
         }
-
-        final char[] chars = this.chars;
         chars[off++] = quote;
         off = IOUtils.writeLocalTime(chars, off, time);
         chars[off] = quote;
@@ -2739,11 +2736,10 @@ class JSONWriterUTF16
 
         int off = this.off;
         int minCapacity = off + zoneSize + 38;
-        if (minCapacity >= chars.length) {
-            ensureCapacity(minCapacity);
+        char[] chars = this.chars;
+        if (minCapacity > chars.length) {
+            chars = grow(minCapacity);
         }
-
-        final char[] chars = this.chars;
         chars[off] = quote;
         LocalDate localDate = dateTime.toLocalDate();
         off = IOUtils.writeLocalDate(chars, off + 1, localDate.getYear(), localDate.getMonthValue(), localDate.getDayOfMonth());
@@ -2773,11 +2769,10 @@ class JSONWriterUTF16
 
         int off = this.off;
         int minCapacity = off + 45;
-        if (minCapacity >= chars.length) {
-            ensureCapacity(minCapacity);
+        char[] chars = this.chars;
+        if (minCapacity > chars.length) {
+            chars = grow(minCapacity);
         }
-
-        final char[] chars = this.chars;
         chars[off] = quote;
         LocalDateTime ldt = dateTime.toLocalDateTime();
         LocalDate date = ldt.toLocalDate();
@@ -2807,11 +2802,10 @@ class JSONWriterUTF16
         ZoneOffset offset = time.getOffset();
         int off = this.off;
         int minCapacity = off + 25;
-        if (minCapacity >= chars.length) {
-            ensureCapacity(minCapacity);
+        char[] chars = this.chars;
+        if (minCapacity > chars.length) {
+            chars = grow(minCapacity);
         }
-
-        final char[] chars = this.chars;
         chars[off] = quote;
         off = IOUtils.writeLocalTime(chars, off + 1, time.toLocalTime());
         if (offset.getTotalSeconds() == 0) {
@@ -2946,6 +2940,11 @@ class JSONWriterUTF16
 
     @Override
     public final void write(JSONObject map) {
+        if (pretty != PRETTY_NON) {
+            super.write(map);
+            return;
+        }
+
         if (map == null) {
             this.writeNull();
             return;
@@ -2963,7 +2962,7 @@ class JSONWriterUTF16
         }
 
         if (off == chars.length) {
-            ensureCapacity(off + 1);
+            grow(off + 1);
         }
         chars[off++] = '{';
 
@@ -2976,7 +2975,7 @@ class JSONWriterUTF16
 
             if (!first) {
                 if (off == chars.length) {
-                    ensureCapacity(off + 1);
+                    grow(off + 1);
                 }
                 chars[off++] = ',';
             }
@@ -2990,7 +2989,7 @@ class JSONWriterUTF16
             }
 
             if (off == chars.length) {
-                ensureCapacity(off + 1);
+                grow(off + 1);
             }
             chars[off++] = ':';
 
@@ -3039,10 +3038,7 @@ class JSONWriterUTF16
             objectWriter.write(this, value, null, null, 0);
         }
 
-        if (off == chars.length) {
-            ensureCapacity(off + 1);
-        }
-        chars[off++] = '}';
+        endObject();
     }
 
     @Override
@@ -3073,7 +3069,7 @@ class JSONWriterUTF16
             Object o = array.get(i);
             if (!first) {
                 if (off == chars.length) {
-                    ensureCapacity(off + 1);
+                    grow(off + 1);
                 }
                 chars[off++] = ',';
             }
@@ -3124,7 +3120,7 @@ class JSONWriterUTF16
             objectWriter.write(this, o, null, null, 0);
         }
         if (off == chars.length) {
-            ensureCapacity(off + 1);
+            grow(off + 1);
         }
         chars[off++] = ']';
     }
@@ -3186,21 +3182,21 @@ class JSONWriterUTF16
 
     private void writeQuote() {
         if (off == chars.length) {
-            ensureCapacity(off + 1);
+            grow(off + 1);
         }
         chars[off++] = quote;
     }
 
-    public final void writeString(final char[] chars) {
-        if (chars == null) {
+    public final void writeString(final char[] str) {
+        if (str == null) {
             writeStringNull();
             return;
         }
 
         boolean browserSecure = (context.features & BrowserSecure.mask) != 0;
         boolean special = (context.features & EscapeNoneAscii.mask) != 0;
-        for (int i = 0; i < chars.length; i++) {
-            char c = chars[i];
+        for (int i = 0; i < str.length; i++) {
+            char c = str[i];
             if (c == '\\' || c == quote || c < ' ') {
                 special = true;
                 break;
@@ -3214,30 +3210,33 @@ class JSONWriterUTF16
 
         if (!special) {
             // inline ensureCapacity
-            int minCapacity = this.off + chars.length + 2;
-            if (minCapacity > this.chars.length) {
-                ensureCapacity(minCapacity);
+            int off = this.off;
+            int minCapacity = off + str.length + 2;
+            char[] chars = this.chars;
+            if (minCapacity > chars.length) {
+                chars = grow(minCapacity);
             }
 
-            this.chars[this.off++] = quote;
-            System.arraycopy(chars, 0, this.chars, this.off, chars.length);
-            this.off += chars.length;
-            this.chars[this.off++] = quote;
+            chars[off++] = quote;
+            System.arraycopy(str, 0, chars, off, str.length);
+            off += str.length;
+            chars[off] = quote;
+            this.off = off + 1;
             return;
         }
 
-        writeStringEscape(chars);
+        writeStringEscape(str);
     }
 
-    public final void writeString(char[] chars, int off, int len) {
-        if (chars == null) {
+    public final void writeString(char[] str, int coff, int len) {
+        if (str == null) {
             writeStringNull();
             return;
         }
 
         boolean special = (context.features & EscapeNoneAscii.mask) != 0;
-        for (int i = off; i < len; ++i) {
-            char ch = chars[i];
+        for (int i = coff; i < len; ++i) {
+            char ch = str[i];
             if (ch == '\\' || ch == quote || ch < ' ') {
                 special = true;
                 break;
@@ -3246,28 +3245,31 @@ class JSONWriterUTF16
 
         if (!special) {
             // inline ensureCapacity
-            int minCapacity = this.off + len + 2;
-            if (minCapacity >= this.chars.length) {
-                ensureCapacity(minCapacity);
+            int off = this.off;
+            int minCapacity = off + len + 2;
+            char[] chars = this.chars;
+            if (minCapacity > chars.length) {
+                chars = grow(minCapacity);
             }
 
-            this.chars[this.off++] = quote;
-            System.arraycopy(chars, off, this.chars, this.off, len);
-            this.off += len;
-            this.chars[this.off++] = quote;
+            chars[off++] = quote;
+            System.arraycopy(str, coff, chars, off, len);
+            off += len;
+            chars[off] = quote;
+            this.off = off + 1;
             return;
         }
 
-        writeStringEscape(new String(chars, off, len));
+        writeStringEscape(new String(str, coff, len));
     }
 
     public void writeBool(boolean value) {
         int minCapacity = off + 5;
-        if (minCapacity >= this.chars.length) {
-            ensureCapacity(minCapacity);
+        char[] chars = this.chars;
+        if (minCapacity > chars.length) {
+            chars = grow(minCapacity);
         }
 
-        char[] chars = this.chars;
         int off = this.off;
         if ((context.features & WriteBooleanAsNumber.mask) != 0) {
             chars[off++] = value ? '1' : '0';
diff --git a/core/src/main/java/com/alibaba/fastjson2/JSONWriterUTF8.java b/core/src/main/java/com/alibaba/fastjson2/JSONWriterUTF8.java
index 8fa50d841..ae1c12023 100644
--- a/core/src/main/java/com/alibaba/fastjson2/JSONWriterUTF8.java
+++ b/core/src/main/java/com/alibaba/fastjson2/JSONWriterUTF8.java
@@ -58,13 +58,13 @@ class JSONWriterUTF8
     }
 
     public final void writeNull() {
-        int minCapacity = off + 4;
-        if (minCapacity >= this.bytes.length) {
-            ensureCapacity(minCapacity);
+        int off = this.off;
+        byte[] bytes = this.bytes;
+        if (off + 4 > bytes.length) {
+            bytes = grow(off + 4);
         }
-
         UNSAFE.putInt(bytes, ARRAY_BYTE_BASE_OFFSET + off, NULL_32);
-        off += 4;
+        this.off = off + 4;
     }
 
     @Override
@@ -75,7 +75,7 @@ class JSONWriterUTF8
         writeString(path);
         int off = this.off;
         if (off == bytes.length) {
-            ensureCapacity(off + 1);
+            grow(off + 1);
         }
         bytes[off] = '}';
         this.off = off + 1;
@@ -86,8 +86,11 @@ class JSONWriterUTF8
         int charsLen = ((value.length - 1) / 3 + 1) << 2; // base64 character count
 
         int off = this.off;
-        ensureCapacity(off + charsLen + 2);
-        final byte[] bytes = this.bytes;
+        int minCapacity = off + charsLen + 2;
+        byte[] bytes = this.bytes;
+        if (minCapacity > bytes.length) {
+            bytes = grow(minCapacity);
+        }
         bytes[off++] = (byte) quote;
 
         int eLen = (value.length / 3) * 3; // Length of even 24-bits.
@@ -132,8 +135,10 @@ class JSONWriterUTF8
         int charsLen = values.length * 2 + 3;
 
         int off = this.off;
-        ensureCapacity(off + charsLen + 2);
-        final byte[] bytes = this.bytes;
+        int minCapacity = off + charsLen + 2;
+        if (minCapacity > bytes.length) {
+            bytes = grow(minCapacity);
+        }
         bytes[off] = 'x';
         bytes[off + 1] = '\'';
         off += 2;
@@ -222,20 +227,15 @@ class JSONWriterUTF8
         startObject = true;
 
         int off = this.off;
-        int minCapacity = off + (pretty ? 3 + indent : 1);
-        if (minCapacity >= bytes.length) {
-            ensureCapacity(minCapacity);
+        int minCapacity = off + 3 + pretty * level;
+        byte[] bytes = this.bytes;
+        if (minCapacity > bytes.length) {
+            bytes = grow(minCapacity);
         }
-
-        final byte[] bytes = this.bytes;
         bytes[off++] = (byte) '{';
 
-        if (pretty) {
-            indent++;
-            bytes[off++] = (byte) '\n';
-            for (int i = 0; i < indent; ++i) {
-                bytes[off++] = (byte) '\t';
-            }
+        if (pretty != PRETTY_NON) {
+            off = indent(bytes, off);
         }
         this.off = off;
     }
@@ -244,18 +244,13 @@ class JSONWriterUTF8
     public final void endObject() {
         level--;
         int off = this.off;
-        int minCapacity = off + (pretty ? 2 + indent : 1);
-        if (minCapacity >= bytes.length) {
-            ensureCapacity(minCapacity);
+        int minCapacity = off + 1 + (pretty == 0 ? 0 : pretty * level + 1);
+        byte[] bytes = this.bytes;
+        if (minCapacity > bytes.length) {
+            bytes = grow(minCapacity);
         }
-
-        final byte[] bytes = this.bytes;
-        if (pretty) {
-            indent--;
-            bytes[off++] = (byte) '\n';
-            for (int i = 0; i < indent; ++i) {
-                bytes[off++] = (byte) '\t';
-            }
+        if (pretty != PRETTY_NON) {
+            off = indent(bytes, off);
         }
 
         bytes[off] = (byte) '}';
@@ -267,18 +262,14 @@ class JSONWriterUTF8
     public final void writeComma() {
         startObject = false;
         int off = this.off;
-        int minCapacity = off + (pretty ? 2 + indent : 1);
-        if (minCapacity >= bytes.length) {
-            ensureCapacity(minCapacity);
+        int minCapacity = off + 2 + pretty * level;
+        byte[] bytes = this.bytes;
+        if (minCapacity > bytes.length) {
+            bytes = grow(minCapacity);
         }
-
-        final byte[] bytes = this.bytes;
         bytes[off++] = ',';
-        if (pretty) {
-            bytes[off++] = (byte) '\n';
-            for (int i = 0; i < indent; ++i) {
-                bytes[off++] = '\t';
-            }
+        if (pretty != PRETTY_NON) {
+            off = indent(bytes, off);
         }
         this.off = off;
     }
@@ -291,19 +282,14 @@ class JSONWriterUTF8
 
         level++;
         int off = this.off;
-        int minCapacity = off + (pretty ? 3 + indent : 1);
-        if (minCapacity >= bytes.length) {
-            ensureCapacity(minCapacity);
+        int minCapacity = off + 3 + pretty * level;
+        byte[] bytes = this.bytes;
+        if (minCapacity > bytes.length) {
+            bytes = grow(minCapacity);
         }
-
-        final byte[] bytes = this.bytes;
         bytes[off++] = (byte) '[';
-        if (pretty) {
-            indent++;
-            bytes[off++] = (byte) '\n';
-            for (int i = 0; i < indent; ++i) {
-                bytes[off++] = (byte) '\t';
-            }
+        if (pretty != PRETTY_NON) {
+            off = indent(bytes, off);
         }
         this.off = off;
     }
@@ -312,18 +298,13 @@ class JSONWriterUTF8
     public final void endArray() {
         level--;
         int off = this.off;
-        int minCapacity = off + (pretty ? 2 + indent : 1);
-        if (minCapacity >= bytes.length) {
-            ensureCapacity(minCapacity);
+        int minCapacity = off + 1 + (pretty == 0 ? 0 : pretty * level + 1);
+        byte[] bytes = this.bytes;
+        if (minCapacity > bytes.length) {
+            bytes = grow(minCapacity);
         }
-
-        final byte[] bytes = this.bytes;
-        if (pretty) {
-            indent--;
-            bytes[off++] = (byte) '\n';
-            for (int i = 0; i < indent; ++i) {
-                bytes[off++] = (byte) '\t';
-            }
+        if (pretty != PRETTY_NON) {
+            off = indent(bytes, off);
         }
         bytes[off] = (byte) ']';
         this.off = off + 1;
@@ -331,10 +312,14 @@ class JSONWriterUTF8
     }
 
     public final void writeString(List<String> list) {
+        if (pretty != PRETTY_NON) {
+            super.writeString(list);
+            return;
+        }
         // startArray();
         int off = this.off;
         if (off == bytes.length) {
-            ensureCapacity(off + 1);
+            grow(off + 1);
         }
         bytes[off] = '[';
         this.off = off + 1;
@@ -343,7 +328,7 @@ class JSONWriterUTF8
             if (i != 0) {
                 off = this.off;
                 if (off == bytes.length) {
-                    ensureCapacity(off + 1);
+                    grow(off + 1);
                 }
                 bytes[off] = ',';
                 this.off = off + 1;
@@ -419,7 +404,7 @@ class JSONWriterUTF8
 
     private void writeQuote() {
         if (off == bytes.length) {
-            ensureCapacity(off + 1);
+            grow(off + 1);
         }
         bytes[off++] = (byte) quote;
     }
@@ -446,11 +431,11 @@ class JSONWriterUTF8
             minCapacity += chars.length * 3;
         }
 
-        if (minCapacity >= this.bytes.length) {
-            ensureCapacity(minCapacity);
+        byte[] bytes = this.bytes;
+        if (minCapacity > bytes.length) {
+            bytes = grow(minCapacity);
         }
 
-        final byte[] bytes = this.bytes;
         bytes[off++] = (byte) quote;
 
         int i = 0;
@@ -501,10 +486,10 @@ class JSONWriterUTF8
         int off = this.off;
         if (!escape) {
             int minCapacity = off + values.length + 2;
-            if (minCapacity >= this.bytes.length) {
-                ensureCapacity(minCapacity);
+            byte[] bytes = this.bytes;
+            if (minCapacity > bytes.length) {
+                bytes = grow(minCapacity);
             }
-            final byte[] bytes = this.bytes;
             bytes[off] = quote;
             System.arraycopy(values, 0, bytes, off + 1, values.length);
             off += values.length + 1;
@@ -537,10 +522,10 @@ class JSONWriterUTF8
         int off = this.off;
         if (!escape) {
             int minCapacity = off + values.length + 2;
-            if (minCapacity >= this.bytes.length) {
-                ensureCapacity(minCapacity);
+            byte[] bytes = this.bytes;
+            if (minCapacity > bytes.length) {
+                bytes = grow(minCapacity);
             }
-            final byte[] bytes = this.bytes;
             bytes[off] = quote;
             System.arraycopy(values, 0, bytes, off + 1, values.length);
             off += values.length + 1;
@@ -566,11 +551,10 @@ class JSONWriterUTF8
             minCapacity += value.length * 2;
         }
 
-        if (minCapacity >= this.bytes.length) {
-            ensureCapacity(minCapacity);
+        byte[] bytes = this.bytes;
+        if (minCapacity > bytes.length) {
+            bytes = grow(minCapacity);
         }
-
-        final byte[] bytes = this.bytes;
         bytes[off++] = (byte) quote;
 
         int valueOffset = 0;
@@ -779,11 +763,10 @@ class JSONWriterUTF8
             minCapacity += chars.length * 3;
         }
 
-        if (minCapacity >= this.bytes.length) {
-            ensureCapacity(minCapacity);
+        byte[] bytes = this.bytes;
+        if (minCapacity > bytes.length) {
+            bytes = grow(minCapacity);
         }
-
-        final byte[] bytes = this.bytes;
         bytes[off++] = (byte) quote;
 
         int i = 0;
@@ -802,12 +785,6 @@ class JSONWriterUTF8
         }
 
         this.off = off;
-        int rest = chars.length - i;
-        minCapacity = off + rest * 6 + 2;
-        if (minCapacity >= this.bytes.length) {
-            ensureCapacity(minCapacity);
-        }
-
         if (i < chars.length) {
             writeStringEscapedRest(chars, chars.length, browserSecure, escapeNoneAscii, i);
         }
@@ -841,11 +818,10 @@ class JSONWriterUTF8
             minCapacity += strlen * 3;
         }
 
-        if (minCapacity >= this.bytes.length) {
-            ensureCapacity(minCapacity);
-        }
-
         byte[] bytes = this.bytes;
+        if (minCapacity > bytes.length) {
+            bytes = grow(minCapacity);
+        }
         bytes[off++] = (byte) quote;
 
         int i = stroff;
@@ -863,13 +839,6 @@ class JSONWriterUTF8
             bytes[off++] = (byte) c0;
         }
         this.off = off;
-
-        int rest = end - i;
-        minCapacity = off + rest * 6 + 2;
-        if (minCapacity >= this.bytes.length) {
-            ensureCapacity(minCapacity);
-        }
-
         if (i < end) {
             writeStringEscapedRest(chars, end, browserSecure, escapeNoneAscii, i);
         }
@@ -879,12 +848,12 @@ class JSONWriterUTF8
 
     protected final void writeStringEscaped(byte[] values) {
         int minCapacity = off + values.length * 6 + 2;
-        if (minCapacity >= this.bytes.length) {
-            ensureCapacity(minCapacity);
+        byte[] bytes = this.bytes;
+        if (minCapacity > bytes.length) {
+            bytes = grow(minCapacity);
         }
 
         final boolean browserSecure = (context.features & BrowserSecure.mask) != 0;
-        final byte[] bytes = this.bytes;
         int off = this.off;
         bytes[off++] = (byte) quote;
         for (int i = 0; i < values.length; i++) {
@@ -1025,11 +994,11 @@ class JSONWriterUTF8
     ) {
         int rest = chars.length - i;
         int minCapacity = off + rest * 6 + 2;
-        if (minCapacity >= this.bytes.length) {
-            ensureCapacity(minCapacity);
+        byte[] bytes = this.bytes;
+        if (minCapacity > bytes.length) {
+            bytes = grow(minCapacity);
         }
 
-        final byte[] bytes = this.bytes;
         int off = this.off;
         for (; i < end; ++i) { // ascii none special fast write
             char ch = chars[i];
@@ -1219,11 +1188,10 @@ class JSONWriterUTF8
             minCapacity += len * 3;
         }
 
-        if (minCapacity >= this.bytes.length) {
-            ensureCapacity(minCapacity);
+        byte[] bytes = this.bytes;
+        if (minCapacity > bytes.length) {
+            bytes = grow(minCapacity);
         }
-
-        final byte[] bytes = this.bytes;
         int off = this.off;
         if (quoted) {
             bytes[off++] = (byte) quote;
@@ -1443,12 +1411,10 @@ class JSONWriterUTF8
     @Override
     public final void writeChar(char ch) {
         int off = this.off;
-        int minCapacity = off + 8;
-        if (minCapacity >= bytes.length) {
-            ensureCapacity(minCapacity);
+        byte[] bytes = this.bytes;
+        if (off + 8 > bytes.length) {
+            bytes = grow(off + 8);
         }
-
-        final byte[] bytes = this.bytes;
         bytes[off++] = (byte) quote;
         if (ch <= 0x007F) {
             switch (ch) {
@@ -1602,47 +1568,46 @@ class JSONWriterUTF8
         long msb = value.getMostSignificantBits();
         long lsb = value.getLeastSignificantBits();
 
+        int off = this.off;
         int minCapacity = off + 38;
-        if (minCapacity >= bytes.length) {
-            ensureCapacity(minCapacity);
+        byte[] bytes = this.bytes;
+        if (minCapacity > bytes.length) {
+            bytes = grow(minCapacity);
         }
-
-        final byte[] buf = this.bytes;
-        final int off = this.off;
-        buf[off] = '"';
+        bytes[off] = '"';
 
         final long base = ARRAY_BYTE_BASE_OFFSET + off;
 
         UNSAFE.putLong(
-                buf,
+                bytes,
                 base + 1,
                 packDigits((int) (msb >> 56), (int) (msb >> 48), (int) (msb >> 40), (int) (msb >> 32))
         );
-        buf[off + 9] = '-';
+        bytes[off + 9] = '-';
         UNSAFE.putLong(
-                buf,
+                bytes,
                 base + 10,
                 packDigits(((int) msb) >> 24, ((int) msb) >> 16));
-        buf[off + 14] = '-';
+        bytes[off + 14] = '-';
         UNSAFE.putLong(
-                buf,
+                bytes,
                 base + 15,
                 packDigits(((int) msb) >> 8, (int) msb));
-        buf[off + 19] = '-';
+        bytes[off + 19] = '-';
         UNSAFE.putLong(
-                buf,
+                bytes,
                 base + 20,
                 packDigits((int) (lsb >> 56), (int) (lsb >> 48)));
-        buf[off + 24] = '-';
+        bytes[off + 24] = '-';
         UNSAFE.putLong(
-                buf,
+                bytes,
                 base + 25,
                 packDigits(((int) (lsb >> 40)), (int) (lsb >> 32), ((int) lsb) >> 24, ((int) lsb) >> 16));
         UNSAFE.putLong(
-                buf,
+                bytes,
                 base + 33,
                 packDigits(((int) lsb) >> 8, (int) lsb));
-        buf[off + 37] = '"';
+        bytes[off + 37] = '"';
         this.off += 38;
     }
 
@@ -1653,11 +1618,10 @@ class JSONWriterUTF8
         int minCapacity = off
                 + chars.length * 3; // utf8 3 bytes
 
-        if (minCapacity >= this.bytes.length) {
-            ensureCapacity(minCapacity);
+        byte[] bytes = this.bytes;
+        if (minCapacity > bytes.length) {
+            bytes = grow(minCapacity);
         }
-
-        final byte[] bytes = this.bytes;
         for (int i = 0; i < chars.length; i++) {
             char c = chars[i];
             if ((c >= 0x0001) && (c <= 0x007F)) {
@@ -1677,29 +1641,30 @@ class JSONWriterUTF8
     }
 
     @Override
-    public final void writeRaw(byte[] bytes) {
-        int minCapacity = off + bytes.length;
-        if (minCapacity >= this.bytes.length) {
-            ensureCapacity(minCapacity);
+    public final void writeRaw(byte[] raw) {
+        int off = this.off;
+        int minCapacity = off + raw.length;
+        if (minCapacity > bytes.length) {
+            grow(minCapacity);
         }
-        System.arraycopy(bytes, 0, this.bytes, this.off, bytes.length);
-        off += bytes.length;
+        System.arraycopy(raw, 0, bytes, off, raw.length);
+        this.off = off + raw.length;
     }
 
     @Override
     public final void writeNameRaw(byte[] name) {
         int off = this.off;
-        int minCapacity = off + name.length + 2 + indent;
-        if (minCapacity >= this.bytes.length) {
-            ensureCapacity(minCapacity);
+        int minCapacity = off + name.length + 2 + pretty * level;
+        byte[] bytes = this.bytes;
+        if (minCapacity > bytes.length) {
+            bytes = grow(minCapacity);
         }
         if (startObject) {
             startObject = false;
         } else {
-            final byte[] bytes = this.bytes;
             bytes[off++] = ',';
-            if (pretty) {
-                off = indent(bytes, off, indent);
+            if (pretty != PRETTY_NON) {
+                off = indent(bytes, off);
             }
         }
         System.arraycopy(name, 0, bytes, off, name.length);
@@ -1709,18 +1674,17 @@ class JSONWriterUTF8
     @Override
     public final void writeName2Raw(long name) {
         int off = this.off;
-        int minCapacity = off + 10 + indent;
-        if (minCapacity >= this.bytes.length) {
-            ensureCapacity(minCapacity);
-        }
-
+        int minCapacity = off + 10 + pretty * level;
         byte[] bytes = this.bytes;
+        if (minCapacity > bytes.length) {
+            bytes = grow(minCapacity);
+        }
         if (startObject) {
             startObject = false;
         } else {
             bytes[off++] = ',';
-            if (pretty) {
-                off = indent(bytes, off, indent);
+            if (pretty != PRETTY_NON) {
+                off = indent(bytes, off);
             }
         }
 
@@ -1728,30 +1692,27 @@ class JSONWriterUTF8
         this.off = off + 5;
     }
 
-    private static int indent(byte[] bytes, int off, int indent) {
-        bytes[off++] = '\n';
-        int end = off + indent;
-        while (off < end) {
-            bytes[off++] = '\t';
-        }
-        return off;
+    private int indent(byte[] bytes, int off) {
+        bytes[off] = '\n';
+        int toIndex = off + 1 + pretty * level;
+        Arrays.fill(bytes, off + 1, toIndex, pretty == PRETTY_TAB ? (byte) '\t' : (byte) ' ');
+        return toIndex;
     }
 
     @Override
     public final void writeName3Raw(long name) {
         int off = this.off;
-        int minCapacity = off + 10 + indent;
-        if (minCapacity >= this.bytes.length) {
-            ensureCapacity(minCapacity);
-        }
-
+        int minCapacity = off + 10 + pretty * level;
         byte[] bytes = this.bytes;
+        if (minCapacity > bytes.length) {
+            bytes = grow(minCapacity);
+        }
         if (startObject) {
             startObject = false;
         } else {
             bytes[off++] = ',';
-            if (pretty) {
-                off = indent(bytes, off, indent);
+            if (pretty != PRETTY_NON) {
+                off = indent(bytes, off);
             }
         }
 
@@ -1762,18 +1723,17 @@ class JSONWriterUTF8
     @Override
     public final void writeName4Raw(long name) {
         int off = this.off;
-        int minCapacity = off + 10 + indent;
-        if (minCapacity >= this.bytes.length) {
-            ensureCapacity(minCapacity);
-        }
-
+        int minCapacity = off + 10 + pretty * level;
         byte[] bytes = this.bytes;
+        if (minCapacity > bytes.length) {
+            bytes = grow(minCapacity);
+        }
         if (startObject) {
             startObject = false;
         } else {
             bytes[off++] = ',';
-            if (pretty) {
-                off = indent(bytes, off, indent);
+            if (pretty != PRETTY_NON) {
+                off = indent(bytes, off);
             }
         }
 
@@ -1784,18 +1744,17 @@ class JSONWriterUTF8
     @Override
     public final void writeName5Raw(long name) {
         int off = this.off;
-        int minCapacity = off + 10 + indent;
-        if (minCapacity >= this.bytes.length) {
-            ensureCapacity(minCapacity);
-        }
-
+        int minCapacity = off + 10 + pretty * level;
         byte[] bytes = this.bytes;
+        if (minCapacity > bytes.length) {
+            bytes = grow(minCapacity);
+        }
         if (startObject) {
             startObject = false;
         } else {
             bytes[off++] = ',';
-            if (pretty) {
-                off = indent(bytes, off, indent);
+            if (pretty != PRETTY_NON) {
+                off = indent(bytes, off);
             }
         }
 
@@ -1806,18 +1765,17 @@ class JSONWriterUTF8
     @Override
     public final void writeName6Raw(long name) {
         int off = this.off;
-        int minCapacity = off + 11 + indent;
-        if (minCapacity >= this.bytes.length) {
-            ensureCapacity(minCapacity);
+        int minCapacity = off + 11 + pretty * level;
+        byte[] bytes = this.bytes;
+        if (minCapacity > bytes.length) {
+            bytes = grow(minCapacity);
         }
-
-        final byte[] bytes = this.bytes;
         if (startObject) {
             startObject = false;
         } else {
             bytes[off++] = ',';
-            if (pretty) {
-                off = indent(bytes, off, indent);
+            if (pretty != PRETTY_NON) {
+                off = indent(bytes, off);
             }
         }
 
@@ -1829,18 +1787,17 @@ class JSONWriterUTF8
     @Override
     public final void writeName7Raw(long name) {
         int off = this.off;
-        int minCapacity = off + 12 + indent;
-        if (minCapacity >= this.bytes.length) {
-            ensureCapacity(minCapacity);
+        int minCapacity = off + 12 + pretty * level;
+        byte[] bytes = this.bytes;
+        if (minCapacity > bytes.length) {
+            bytes = grow(minCapacity);
         }
-
-        final byte[] bytes = this.bytes;
         if (startObject) {
             startObject = false;
         } else {
             bytes[off++] = ',';
-            if (pretty) {
-                off = indent(bytes, off, indent);
+            if (pretty != PRETTY_NON) {
+                off = indent(bytes, off);
             }
         }
 
@@ -1853,18 +1810,17 @@ class JSONWriterUTF8
     @Override
     public final void writeName8Raw(long name) {
         int off = this.off;
-        int minCapacity = off + 13 + indent;
-        if (minCapacity >= this.bytes.length) {
-            ensureCapacity(minCapacity);
+        int minCapacity = off + 13 + pretty * level;
+        byte[] bytes = this.bytes;
+        if (minCapacity > bytes.length) {
+            bytes = grow(minCapacity);
         }
-
-        final byte[] bytes = this.bytes;
         if (startObject) {
             startObject = false;
         } else {
             bytes[off++] = ',';
-            if (pretty) {
-                off = indent(bytes, off, indent);
+            if (pretty != PRETTY_NON) {
+                off = indent(bytes, off);
             }
         }
 
@@ -1878,18 +1834,17 @@ class JSONWriterUTF8
     @Override
     public final void writeName9Raw(long name0, int name1) {
         int off = this.off;
-        int minCapacity = off + 14 + indent;
-        if (minCapacity >= this.bytes.length) {
-            ensureCapacity(minCapacity);
+        int minCapacity = off + 14 + pretty * level;
+        byte[] bytes = this.bytes;
+        if (minCapacity > bytes.length) {
+            bytes = grow(minCapacity);
         }
-
-        final byte[] bytes = this.bytes;
         if (startObject) {
             startObject = false;
         } else {
             bytes[off++] = ',';
-            if (pretty) {
-                off = indent(bytes, off, indent);
+            if (pretty != PRETTY_NON) {
+                off = indent(bytes, off);
             }
         }
 
@@ -1901,18 +1856,17 @@ class JSONWriterUTF8
     @Override
     public final void writeName10Raw(long name0, long name1) {
         int off = this.off;
-        int minCapacity = off + 18 + indent;
-        if (minCapacity >= this.bytes.length) {
-            ensureCapacity(minCapacity);
+        int minCapacity = off + 18 + pretty * level;
+        byte[] bytes = this.bytes;
+        if (minCapacity > bytes.length) {
+            bytes = grow(minCapacity);
         }
-
-        final byte[] bytes = this.bytes;
         if (startObject) {
             startObject = false;
         } else {
             bytes[off++] = ',';
-            if (pretty) {
-                off = indent(bytes, off, indent);
+            if (pretty != PRETTY_NON) {
+                off = indent(bytes, off);
             }
         }
 
@@ -1924,18 +1878,17 @@ class JSONWriterUTF8
     @Override
     public final void writeName11Raw(long name0, long name1) {
         int off = this.off;
-        int minCapacity = off + 18 + indent;
-        if (minCapacity >= this.bytes.length) {
-            ensureCapacity(minCapacity);
+        int minCapacity = off + 18 + pretty * level;
+        byte[] bytes = this.bytes;
+        if (minCapacity > bytes.length) {
+            bytes = grow(minCapacity);
         }
-
-        final byte[] bytes = this.bytes;
         if (startObject) {
             startObject = false;
         } else {
             bytes[off++] = ',';
-            if (pretty) {
-                off = indent(bytes, off, indent);
+            if (pretty != PRETTY_NON) {
+                off = indent(bytes, off);
             }
         }
 
@@ -1947,18 +1900,17 @@ class JSONWriterUTF8
     @Override
     public final void writeName12Raw(long name0, long name1) {
         int off = this.off;
-        int minCapacity = off + 18 + indent;
-        if (minCapacity >= this.bytes.length) {
-            ensureCapacity(minCapacity);
+        int minCapacity = off + 18 + pretty * level;
+        byte[] bytes = this.bytes;
+        if (minCapacity > bytes.length) {
+            bytes = grow(minCapacity);
         }
-
-        final byte[] bytes = this.bytes;
         if (startObject) {
             startObject = false;
         } else {
             bytes[off++] = ',';
-            if (pretty) {
-                off = indent(bytes, off, indent);
+            if (pretty != PRETTY_NON) {
+                off = indent(bytes, off);
             }
         }
 
@@ -1970,18 +1922,17 @@ class JSONWriterUTF8
     @Override
     public final void writeName13Raw(long name0, long name1) {
         int off = this.off;
-        int minCapacity = off + 18 + indent;
-        if (minCapacity >= this.bytes.length) {
-            ensureCapacity(minCapacity);
+        int minCapacity = off + 18 + pretty * level;
+        byte[] bytes = this.bytes;
+        if (minCapacity > bytes.length) {
+            bytes = grow(minCapacity);
         }
-
-        final byte[] bytes = this.bytes;
         if (startObject) {
             startObject = false;
         } else {
             bytes[off++] = ',';
-            if (pretty) {
-                off = indent(bytes, off, indent);
+            if (pretty != PRETTY_NON) {
+                off = indent(bytes, off);
             }
         }
 
@@ -1993,18 +1944,17 @@ class JSONWriterUTF8
     @Override
     public final void writeName14Raw(long name0, long name1) {
         int off = this.off;
-        int minCapacity = off + 19 + indent;
-        if (minCapacity >= this.bytes.length) {
-            ensureCapacity(minCapacity);
+        int minCapacity = off + 19 + pretty * level;
+        byte[] bytes = this.bytes;
+        if (minCapacity > bytes.length) {
+            bytes = grow(minCapacity);
         }
-
-        final byte[] bytes = this.bytes;
         if (startObject) {
             startObject = false;
         } else {
             bytes[off++] = ',';
-            if (pretty) {
-                off = indent(bytes, off, indent);
+            if (pretty != PRETTY_NON) {
+                off = indent(bytes, off);
             }
         }
 
@@ -2017,18 +1967,17 @@ class JSONWriterUTF8
     @Override
     public final void writeName15Raw(long name0, long name1) {
         int off = this.off;
-        int minCapacity = off + 20 + indent;
-        if (minCapacity >= this.bytes.length) {
-            ensureCapacity(minCapacity);
+        int minCapacity = off + 20 + pretty * level;
+        byte[] bytes = this.bytes;
+        if (minCapacity > bytes.length) {
+            bytes = grow(minCapacity);
         }
-
-        final byte[] bytes = this.bytes;
         if (startObject) {
             startObject = false;
         } else {
             bytes[off++] = ',';
-            if (pretty) {
-                off = indent(bytes, off, indent);
+            if (pretty != PRETTY_NON) {
+                off = indent(bytes, off);
             }
         }
 
@@ -2042,18 +1991,17 @@ class JSONWriterUTF8
     @Override
     public final void writeName16Raw(long name0, long name1) {
         int off = this.off;
-        int minCapacity = off + 21 + indent;
-        if (minCapacity >= this.bytes.length) {
-            ensureCapacity(minCapacity);
+        int minCapacity = off + 21 + pretty * level;
+        byte[] bytes = this.bytes;
+        if (minCapacity > bytes.length) {
+            bytes = grow(minCapacity);
         }
-
-        final byte[] bytes = this.bytes;
         if (startObject) {
             startObject = false;
         } else {
             bytes[off++] = ',';
-            if (pretty) {
-                off = indent(bytes, off, indent);
+            if (pretty != PRETTY_NON) {
+                off = indent(bytes, off);
             }
         }
 
@@ -2079,16 +2027,14 @@ class JSONWriterUTF8
 
     @Override
     public final void writeRaw(char c0, char c1) {
-        if (c0 > 128) {
-            throw new JSONException("not support " + c0);
-        }
-        if (c1 > 128) {
-            throw new JSONException("not support " + c1);
+        if (c0 > 128 || c1 > 128) {
+            throw new JSONException("not support " + c0 + ", " + c1);
         }
 
         int off = this.off;
-        if (off + 1 >= bytes.length) {
-            ensureCapacity(off + 2);
+        byte[] bytes = this.bytes;
+        if (off + 2 > bytes.length) {
+            bytes = grow(off + 2);
         }
         bytes[off] = (byte) c0;
         bytes[off + 1] = (byte) c1;
@@ -2096,35 +2042,43 @@ class JSONWriterUTF8
     }
 
     @Override
-    public final void writeNameRaw(byte[] bytes, int off, int len) {
-        int minCapacity = this.off + len + 2 + indent;
-        if (minCapacity >= this.bytes.length) {
-            ensureCapacity(minCapacity);
+    public final void writeNameRaw(byte[] name, int coff, int len) {
+        int off = this.off;
+        int minCapacity = off + len + 2 + pretty * level;
+        byte[] bytes = this.bytes;
+        if (minCapacity > bytes.length) {
+            bytes = grow(minCapacity);
         }
 
-        if (startObject) {
-            startObject = false;
-        } else {
-            writeComma();
+        if (!startObject) {
+            bytes[off++] = ',';
+            if (pretty != PRETTY_NON) {
+                off = indent(bytes, off);
+            }
         }
-        System.arraycopy(bytes, off, this.bytes, this.off, len);
-        this.off += len;
+        startObject = false;
+        System.arraycopy(name, coff, bytes, off, len);
+        this.off = off + len;
     }
 
     final void ensureCapacity(int minCapacity) {
-        if (minCapacity >= bytes.length) {
-            int oldCapacity = bytes.length;
-            int newCapacity = oldCapacity + (oldCapacity >> 1);
-            if (newCapacity - minCapacity < 0) {
-                newCapacity = minCapacity;
-            }
-            if (newCapacity - maxArraySize > 0) {
-                throw new OutOfMemoryError("try enabling LargeObject feature instead");
-            }
+        if (minCapacity > bytes.length) {
+            grow(minCapacity);
+        }
+    }
 
-            // minCapacity is usually close to size, so this is a win:
-            bytes = Arrays.copyOf(bytes, newCapacity);
+    private byte[] grow(int minCapacity) {
+        int oldCapacity = bytes.length;
+        int newCapacity = oldCapacity + (oldCapacity >> 1);
+        if (newCapacity - minCapacity < 0) {
+            newCapacity = minCapacity;
+        }
+        if (newCapacity - maxArraySize > 0) {
+            throw new OutOfMemoryError("try enabling LargeObject feature instead");
         }
+
+        // minCapacity is usually close to size, so this is a win:
+        return bytes = Arrays.copyOf(bytes, newCapacity);
     }
 
     public final void writeInt32(int[] values) {
@@ -2137,11 +2091,10 @@ class JSONWriterUTF8
 
         int off = this.off;
         int minCapacity = off + values.length * 13 + 2;
-        if (minCapacity >= bytes.length) {
-            ensureCapacity(minCapacity);
+        byte[] bytes = this.bytes;
+        if (minCapacity > bytes.length) {
+            bytes = grow(minCapacity);
         }
-
-        final byte[] bytes = this.bytes;
         bytes[off++] = '[';
 
         for (int i = 0; i < values.length; i++) {
@@ -2167,11 +2120,10 @@ class JSONWriterUTF8
 
         int off = this.off;
         int minCapacity = off + 5;
-        if (minCapacity >= bytes.length) {
-            ensureCapacity(minCapacity);
+        byte[] bytes = this.bytes;
+        if (minCapacity > bytes.length) {
+            bytes = grow(minCapacity);
         }
-
-        final byte[] bytes = this.bytes;
         if (writeAsString) {
             bytes[off++] = (byte) quote;
         }
@@ -2193,11 +2145,10 @@ class JSONWriterUTF8
 
         int off = this.off;
         int minCapacity = off + values.length * 5 + 2;
-        if (minCapacity >= bytes.length) {
-            ensureCapacity(minCapacity);
+        byte[] bytes = this.bytes;
+        if (minCapacity > bytes.length) {
+            bytes = grow(minCapacity);
         }
-
-        final byte[] bytes = this.bytes;
         bytes[off++] = '[';
 
         for (int i = 0; i < values.length; i++) {
@@ -2223,11 +2174,10 @@ class JSONWriterUTF8
 
         int off = this.off;
         int minCapacity = off + 7;
-        if (minCapacity >= bytes.length) {
-            ensureCapacity(minCapacity);
+        byte[] bytes = this.bytes;
+        if (minCapacity > bytes.length) {
+            bytes = grow(minCapacity);
         }
-
-        final byte[] bytes = this.bytes;
         if (writeAsString) {
             bytes[off++] = (byte) quote;
         }
@@ -2253,11 +2203,10 @@ class JSONWriterUTF8
 
         int off = this.off;
         int minCapacity = off + 13;
-        if (minCapacity >= bytes.length) {
-            ensureCapacity(minCapacity);
+        byte[] bytes = this.bytes;
+        if (minCapacity > bytes.length) {
+            bytes = grow(minCapacity);
         }
-
-        final byte[] bytes = this.bytes;
         if (writeAsString) {
             bytes[off++] = (byte) quote;
         }
@@ -2279,11 +2228,10 @@ class JSONWriterUTF8
         boolean writeAsString = (context.features & WriteNonStringValueAsString.mask) != 0;
         int off = this.off;
         int minCapacity = off + 2 + size * 23;
-        if (minCapacity >= bytes.length) {
-            ensureCapacity(minCapacity);
+        byte[] bytes = this.bytes;
+        if (minCapacity > bytes.length) {
+            bytes = grow(minCapacity);
         }
-
-        final byte[] bytes = this.bytes;
         bytes[off++] = (byte) '[';
 
         for (int i = 0; i < size; i++) {
@@ -2320,11 +2268,10 @@ class JSONWriterUTF8
 
         int off = this.off;
         int minCapacity = off + 2 + values.length * 23;
-        if (minCapacity >= bytes.length) {
-            ensureCapacity(minCapacity);
+        byte[] bytes = this.bytes;
+        if (minCapacity > bytes.length) {
+            bytes = grow(minCapacity);
         }
-
-        final byte[] bytes = this.bytes;
         bytes[off++] = (byte) '[';
 
         for (int i = 0; i < values.length; i++) {
@@ -2356,11 +2303,10 @@ class JSONWriterUTF8
         int size = values.size();
         int off = this.off;
         int minCapacity = off + 2 + size * 23;
-        if (minCapacity >= bytes.length) {
-            ensureCapacity(minCapacity);
+        byte[] bytes = this.bytes;
+        if (minCapacity > bytes.length) {
+            bytes = grow(minCapacity);
         }
-
-        final byte[] bytes = this.bytes;
         bytes[off++] = (byte) '[';
 
         for (int i = 0; i < size; i++) {
@@ -2395,11 +2341,10 @@ class JSONWriterUTF8
         boolean writeAsString = isWriteAsString(i, features);
         int off = this.off;
         int minCapacity = off + 23;
-        if (minCapacity >= bytes.length) {
-            ensureCapacity(minCapacity);
+        byte[] bytes = this.bytes;
+        if (minCapacity > bytes.length) {
+            bytes = grow(minCapacity);
         }
-
-        final byte[] bytes = this.bytes;
         if (writeAsString) {
             bytes[off++] = (byte) quote;
         }
@@ -2430,8 +2375,9 @@ class JSONWriterUTF8
 
         int off = this.off;
         int minCapacity = off + 17;
-        if (minCapacity >= bytes.length) {
-            ensureCapacity(minCapacity);
+        byte[] bytes = this.bytes;
+        if (minCapacity > bytes.length) {
+            bytes = grow(minCapacity);
         }
 
         if (writeAsString) {
@@ -2453,12 +2399,10 @@ class JSONWriterUTF8
 
         int off = this.off;
         int minCapacity = off + 26;
-
-        if (minCapacity >= bytes.length) {
-            ensureCapacity(minCapacity);
+        byte[] bytes = this.bytes;
+        if (minCapacity > bytes.length) {
+            bytes = grow(minCapacity);
         }
-
-        final byte[] bytes = this.bytes;
         if (writeAsString) {
             bytes[off++] = '"';
         }
@@ -2482,11 +2426,10 @@ class JSONWriterUTF8
 
         int off = this.off;
         int minCapacity = off + values.length * (writeAsString ? 16 : 18) + 1;
-        if (minCapacity >= bytes.length) {
-            ensureCapacity(minCapacity);
+        byte[] bytes = this.bytes;
+        if (minCapacity > bytes.length) {
+            bytes = grow(minCapacity);
         }
-
-        final byte[] bytes = this.bytes;
         bytes[off++] = '[';
         for (int i = 0; i < values.length; i++) {
             if (i != 0) {
@@ -2520,10 +2463,10 @@ class JSONWriterUTF8
 
         int off = this.off;
         int minCapacity = off + values.length * 27 + 1;
-        if (minCapacity >= bytes.length) {
-            ensureCapacity(minCapacity);
+        byte[] bytes = this.bytes;
+        if (minCapacity > bytes.length) {
+            bytes = grow(minCapacity);
         }
-        final byte[] bytes = this.bytes;
         bytes[off++] = '[';
         for (int i = 0; i < values.length; i++) {
             if (i != 0) {
@@ -2557,11 +2500,10 @@ class JSONWriterUTF8
     ) {
         int off = this.off;
         int minCapacity = off + 16;
-        if (minCapacity >= this.bytes.length) {
-            ensureCapacity(minCapacity);
+        byte[] bytes = this.bytes;
+        if (minCapacity > bytes.length) {
+            bytes = grow(minCapacity);
         }
-
-        final byte[] bytes = this.bytes;
         bytes[off] = (byte) quote;
         if (year < 0 || year > 9999) {
             throw illegalYear(year);
@@ -2590,11 +2532,10 @@ class JSONWriterUTF8
             int second) {
         int off = this.off;
         int minCapacity = off + 21;
-        if (minCapacity >= this.bytes.length) {
-            ensureCapacity(minCapacity);
+        byte[] bytes = this.bytes;
+        if (minCapacity > bytes.length) {
+            bytes = grow(minCapacity);
         }
-
-        final byte[] bytes = this.bytes;
         bytes[off] = (byte) quote;
         off = IOUtils.writeLocalDate(bytes, off + 1, year, month, dayOfMonth);
         bytes[off] = ' ';
@@ -2617,10 +2558,10 @@ class JSONWriterUTF8
 
         int off = this.off;
         int minCapacity = off + 18;
-        if (minCapacity >= bytes.length) {
-            ensureCapacity(minCapacity);
+        byte[] bytes = this.bytes;
+        if (minCapacity > bytes.length) {
+            bytes = grow(minCapacity);
         }
-        final byte[] bytes = this.bytes;
         bytes[off++] = (byte) quote;
         off = IOUtils.writeLocalDate(bytes, off, date.getYear(), date.getMonthValue(), date.getDayOfMonth());
         bytes[off] = (byte) quote;
@@ -2631,11 +2572,10 @@ class JSONWriterUTF8
     public final void writeLocalDateTime(LocalDateTime dateTime) {
         int off = this.off;
         int minCapacity = off + 38;
-        if (minCapacity >= bytes.length) {
-            ensureCapacity(minCapacity);
+        byte[] bytes = this.bytes;
+        if (minCapacity > bytes.length) {
+            bytes = grow(minCapacity);
         }
-
-        final byte[] bytes = this.bytes;
         bytes[off++] = (byte) quote;
         LocalDate localDate = dateTime.toLocalDate();
         off = IOUtils.writeLocalDate(bytes, off, localDate.getYear(), localDate.getMonthValue(), localDate.getDayOfMonth());
@@ -2649,11 +2589,10 @@ class JSONWriterUTF8
     public final void writeDateYYYMMDD8(int year, int month, int dayOfMonth) {
         int off = this.off;
         int minCapacity = off + 10;
-        if (minCapacity >= this.bytes.length) {
-            ensureCapacity(minCapacity);
+        byte[] bytes = this.bytes;
+        if (minCapacity > bytes.length) {
+            bytes = grow(minCapacity);
         }
-
-        final byte[] bytes = this.bytes;
         bytes[off] = (byte) quote;
         if (year < 0 || year > 9999) {
             throw illegalYear(year);
@@ -2672,11 +2611,10 @@ class JSONWriterUTF8
     public final void writeDateYYYMMDD10(int year, int month, int dayOfMonth) {
         int off = this.off;
         int minCapacity = off + 13;
-        if (minCapacity >= bytes.length) {
-            ensureCapacity(minCapacity);
+        byte[] bytes = this.bytes;
+        if (minCapacity > bytes.length) {
+            bytes = grow(minCapacity);
         }
-
-        final byte[] bytes = this.bytes;
         bytes[off++] = (byte) quote;
         off = IOUtils.writeLocalDate(bytes, off, year, month, dayOfMonth);
         bytes[off] = (byte) quote;
@@ -2687,11 +2625,10 @@ class JSONWriterUTF8
     public final void writeTimeHHMMSS8(int hour, int minute, int second) {
         int off = this.off;
         int minCapacity = off + 10;
-        if (minCapacity >= this.bytes.length) {
-            ensureCapacity(minCapacity);
+        byte[] bytes = this.bytes;
+        if (minCapacity > bytes.length) {
+            bytes = grow(minCapacity);
         }
-
-        final byte[] bytes = this.bytes;
         bytes[off] = (byte) quote;
         IOUtils.writeLocalTime(bytes, off + 1, hour, minute, second);
         bytes[off + 9] = (byte) quote;
@@ -2702,11 +2639,10 @@ class JSONWriterUTF8
     public final void writeLocalTime(LocalTime time) {
         int off = this.off;
         int minCapacity = off + 20;
-        if (minCapacity >= bytes.length) {
-            ensureCapacity(minCapacity);
+        byte[] bytes = this.bytes;
+        if (minCapacity > bytes.length) {
+            bytes = grow(minCapacity);
         }
-
-        final byte[] bytes = this.bytes;
         bytes[off++] = (byte) quote;
         off = IOUtils.writeLocalTime(bytes, off, time);
         bytes[off] = (byte) quote;
@@ -2736,11 +2672,10 @@ class JSONWriterUTF8
 
         int off = this.off;
         int minCapacity = off + zoneSize + 38;
-        if (minCapacity >= bytes.length) {
-            ensureCapacity(minCapacity);
+        byte[] bytes = this.bytes;
+        if (minCapacity > bytes.length) {
+            bytes = grow(minCapacity);
         }
-
-        final byte[] bytes = this.bytes;
         bytes[off] = (byte) quote;
         LocalDate localDate = dateTime.toLocalDate();
         off = IOUtils.writeLocalDate(bytes, off + 1, localDate.getYear(), localDate.getMonthValue(), localDate.getDayOfMonth());
@@ -2770,11 +2705,10 @@ class JSONWriterUTF8
 
         int off = this.off;
         int minCapacity = off + 45;
-        if (minCapacity >= bytes.length) {
-            ensureCapacity(minCapacity);
+        byte[] bytes = this.bytes;
+        if (minCapacity > bytes.length) {
+            bytes = grow(minCapacity);
         }
-
-        final byte[] bytes = this.bytes;
         bytes[off] = (byte) quote;
         LocalDateTime ldt = dateTime.toLocalDateTime();
         LocalDate date = ldt.toLocalDate();
@@ -2802,11 +2736,10 @@ class JSONWriterUTF8
 
         int off = this.off;
         int minCapacity = off + 45;
-        if (minCapacity >= bytes.length) {
-            ensureCapacity(minCapacity);
+        byte[] bytes = this.bytes;
+        if (minCapacity > bytes.length) {
+            bytes = grow(minCapacity);
         }
-
-        final byte[] bytes = this.bytes;
         bytes[off] = (byte) quote;
         off = IOUtils.writeLocalTime(bytes, off + 1, time.toLocalTime());
 
@@ -2842,11 +2775,10 @@ class JSONWriterUTF8
         int strlen = str.length();
 
         int minCapacity = off + strlen + (writeAsString ? 2 : 0);
-        if (minCapacity >= this.bytes.length) {
-            ensureCapacity(minCapacity);
+        byte[] bytes = this.bytes;
+        if (minCapacity > bytes.length) {
+            bytes = grow(minCapacity);
         }
-
-        final byte[] bytes = this.bytes;
         if (writeAsString) {
             bytes[off++] = '"';
         }
@@ -2877,13 +2809,12 @@ class JSONWriterUTF8
             zonelen = 0;
         }
 
+        int off = this.off;
         int minCapacity = off + 25 + zonelen;
-        if (minCapacity >= bytes.length) {
-            ensureCapacity(minCapacity);
+        byte[] bytes = this.bytes;
+        if (minCapacity > bytes.length) {
+            bytes = grow(minCapacity);
         }
-
-        final byte[] bytes = this.bytes;
-        int off = this.off;
         bytes[off] = (byte) quote;
         off = IOUtils.writeLocalDate(bytes, off + 1, year, month, dayOfMonth);
         bytes[off] = (byte) (timeZone ? 'T' : ' ');
@@ -2951,11 +2882,10 @@ class JSONWriterUTF8
 
         int off = this.off;
         int minCapacity = off + precision + value.scale() + 7;
-        if (minCapacity >= bytes.length) {
-            ensureCapacity(minCapacity);
+        byte[] bytes = this.bytes;
+        if (minCapacity > bytes.length) {
+            bytes = grow(minCapacity);
         }
-
-        final byte[] bytes = this.bytes;
         if (writeAsString) {
             bytes[off++] = '"';
         }
@@ -2994,6 +2924,11 @@ class JSONWriterUTF8
 
     @Override
     public final void write(JSONObject map) {
+        if (pretty != PRETTY_NON) {
+            super.write(map);
+            return;
+        }
+
         if (map == null) {
             this.writeNull();
             return;
@@ -3011,7 +2946,7 @@ class JSONWriterUTF8
         }
 
         if (off == bytes.length) {
-            ensureCapacity(off + 1);
+            grow(off + 1);
         }
         bytes[off++] = '{';
 
@@ -3088,7 +3023,7 @@ class JSONWriterUTF8
         }
 
         if (off == bytes.length) {
-            ensureCapacity(off + 1);
+            grow(off + 1);
         }
         bytes[off++] = '}';
     }
@@ -3112,7 +3047,7 @@ class JSONWriterUTF8
         }
 
         if (off == bytes.length) {
-            ensureCapacity(off + 1);
+            grow(off + 1);
         }
         bytes[off++] = '[';
 
@@ -3121,7 +3056,7 @@ class JSONWriterUTF8
             Object o = array.get(i);
             if (!first) {
                 if (off == bytes.length) {
-                    ensureCapacity(off + 1);
+                    grow(off + 1);
                 }
                 bytes[off++] = ',';
             }
@@ -3172,18 +3107,17 @@ class JSONWriterUTF8
             objectWriter.write(this, o, null, null, 0);
         }
         if (off == bytes.length) {
-            ensureCapacity(off + 1);
+            grow(off + 1);
         }
         bytes[off++] = ']';
     }
 
     public void writeBool(boolean value) {
         int minCapacity = off + 5;
-        if (minCapacity >= this.bytes.length) {
-            ensureCapacity(minCapacity);
-        }
-
         byte[] bytes = this.bytes;
+        if (minCapacity > bytes.length) {
+            bytes = grow(minCapacity);
+        }
         int off = this.off;
         if ((context.features & WriteBooleanAsNumber.mask) != 0) {
             bytes[off++] = (byte) (value ? '1' : '0');
diff --git a/core/src/main/java/com/alibaba/fastjson2/writer/ObjectWriterImplList.java b/core/src/main/java/com/alibaba/fastjson2/writer/ObjectWriterImplList.java
index c1404fbaa..195b3e21b 100644
--- a/core/src/main/java/com/alibaba/fastjson2/writer/ObjectWriterImplList.java
+++ b/core/src/main/java/com/alibaba/fastjson2/writer/ObjectWriterImplList.java
@@ -267,8 +267,14 @@ final class ObjectWriterImplList
         JSONWriter.Context context = jsonWriter.context;
         ObjectWriterProvider provider = context.provider;
 
+        int size = list.size();
+        if (size == 0) {
+            jsonWriter.writeRaw('[', ']');
+            return;
+        }
+
         jsonWriter.startArray();
-        for (int i = 0; i < list.size(); i++) {
+        for (int i = 0; i < size; i++) {
             if (i != 0) {
                 jsonWriter.writeComma();
             }
diff --git a/core/src/main/java/com/alibaba/fastjson2/writer/ObjectWriterImplMap.java b/core/src/main/java/com/alibaba/fastjson2/writer/ObjectWriterImplMap.java
index 9c965e675..fc76b0e91 100644
--- a/core/src/main/java/com/alibaba/fastjson2/writer/ObjectWriterImplMap.java
+++ b/core/src/main/java/com/alibaba/fastjson2/writer/ObjectWriterImplMap.java
@@ -404,19 +404,21 @@ public final class ObjectWriterImplMap
             writeWithFilter(jsonWriter, object, fieldName, fieldType, features);
             return;
         }
+        Map map = (Map) object;
 
         boolean refDetect = jsonWriter.isRefDetect();
+        boolean writeTypeInfo = (fieldType == this.objectType && jsonWriter.isWriteMapTypeInfo(object, objectClass, features))
+                || jsonWriter.isWriteTypeInfo(object, fieldType, features);
+        if (!writeTypeInfo && map.isEmpty()) {
+            jsonWriter.writeRaw('{', '}');
+            return;
+        }
 
         jsonWriter.startObject();
-
-        if ((fieldType == this.objectType && jsonWriter.isWriteMapTypeInfo(object, objectClass, features))
-                || jsonWriter.isWriteTypeInfo(object, fieldType, features)
-        ) {
+        if (writeTypeInfo) {
             writeTypeInfo(jsonWriter);
         }
 
-        Map map = (Map) object;
-
         features |= jsonWriter.getFeatures();
         if ((features & (MapSortField.mask | SortMapEntriesByKeys.mask)) != 0) {
             if (!(map instanceof SortedMap)


Test patch:
diff --git a/core/src/test/java/com/alibaba/fastjson2/JSONWriterTest.java b/core/src/test/java/com/alibaba/fastjson2/JSONWriterTest.java
index 221e4d9c9..7e998b4a3 100644
--- a/core/src/test/java/com/alibaba/fastjson2/JSONWriterTest.java
+++ b/core/src/test/java/com/alibaba/fastjson2/JSONWriterTest.java
@@ -1010,6 +1010,17 @@ public class JSONWriterTest {
         }
     }
 
+    @Test
+    public void writeObjectNonStringKey() {
+        JSONObject object = new JSONObject();
+        ((Map) object).put(123, "123");
+        JSONWriter jsonWriter = JSONWriter.ofUTF8(PrettyFormat);
+        jsonWriter.write(object);
+        assertEquals("{\n" +
+                "\t123:\"123\"\n" +
+                "}", jsonWriter.toString());
+    }
+
     @Test
     public void writeString1() {
         char[] chars = "01234567890".toCharArray();
@@ -2863,4 +2874,33 @@ public class JSONWriterTest {
         context.setFeatures(features);
         assertEquals(features, context.getFeatures());
     }
+
+    @Test
+    public void writeNull() {
+        {
+            JSONWriter jsonWriter = JSONWriter.of();
+            jsonWriter.write((JSONObject) null);
+            assertEquals("null", jsonWriter.toString());
+        }
+        {
+            JSONWriter jsonWriter = JSONWriter.ofUTF8();
+            jsonWriter.write((JSONObject) null);
+            assertEquals("null", jsonWriter.toString());
+        }
+        {
+            JSONWriter jsonWriter = JSONWriter.ofUTF16();
+            jsonWriter.write((JSONObject) null);
+            assertEquals("null", jsonWriter.toString());
+        }
+        {
+            JSONWriter jsonWriter = JSONWriter.ofPretty();
+            jsonWriter.write((JSONObject) null);
+            assertEquals("null", jsonWriter.toString());
+        }
+        {
+            JSONWriter jsonWriter = JSONWriter.ofPretty();
+            jsonWriter.write((Map) null);
+            assertEquals("null", jsonWriter.toString());
+        }
+    }
 }
diff --git a/core/src/test/java/com/alibaba/fastjson2/JSONWriterUTF16Test.java b/core/src/test/java/com/alibaba/fastjson2/JSONWriterUTF16Test.java
index d9d8f8216..389776a07 100644
--- a/core/src/test/java/com/alibaba/fastjson2/JSONWriterUTF16Test.java
+++ b/core/src/test/java/com/alibaba/fastjson2/JSONWriterUTF16Test.java
@@ -644,7 +644,7 @@ public class JSONWriterUTF16Test {
     @Test
     public void writeName8Pretty() {
         JSONWriterUTF16 jsonWriter = new JSONWriterUTF16(JSONFactory.createWriteContext(PrettyFormat));
-        jsonWriter.indent += 2;
+        jsonWriter.level += 2;
 
         char[] name = "a123".toCharArray();
         long nameValue = UNSAFE.getLong(name, ARRAY_CHAR_BASE_OFFSET);
@@ -712,13 +712,14 @@ public class JSONWriterUTF16Test {
 
         char[] name = "a123".toCharArray();
         long nameValue = UNSAFE.getLong(name, ARRAY_CHAR_BASE_OFFSET);
+        byte PRETTY_NON = 0, PRETTY_TAB = 1, PRETTY_SPACE = 3;
 
         final int initOffset = 8183;
         for (int i = 0; i < 16; i++) {
             int initSize = 8196 - i;
             {
                 jsonWriter.startObject = true;
-                jsonWriter.pretty = false;
+                jsonWriter.pretty = PRETTY_NON;
 
                 jsonWriter.chars = new char[initSize];
                 jsonWriter.off = initOffset;
@@ -726,7 +727,7 @@ public class JSONWriterUTF16Test {
             }
             {
                 jsonWriter.startObject = false;
-                jsonWriter.pretty = false;
+                jsonWriter.pretty = PRETTY_NON;
 
                 jsonWriter.chars = new char[initSize];
                 jsonWriter.off = initOffset;
@@ -734,7 +735,7 @@ public class JSONWriterUTF16Test {
             }
             {
                 jsonWriter.startObject = false;
-                jsonWriter.pretty = true;
+                jsonWriter.pretty = PRETTY_TAB;
 
                 jsonWriter.chars = new char[initSize];
                 jsonWriter.off = initOffset;
diff --git a/core/src/test/java/com/alibaba/fastjson2/features/PrettyFormatTest.java b/core/src/test/java/com/alibaba/fastjson2/features/PrettyFormatTest.java
new file mode 100644
index 000000000..da46d322e
--- /dev/null
+++ b/core/src/test/java/com/alibaba/fastjson2/features/PrettyFormatTest.java
@@ -0,0 +1,342 @@
+package com.alibaba.fastjson2.features;
+
+import com.alibaba.fastjson2.JSON;
+import com.alibaba.fastjson2.JSONArray;
+import com.alibaba.fastjson2.JSONObject;
+import com.alibaba.fastjson2.JSONWriter;
+import lombok.Data;
+import org.junit.jupiter.api.Test;
+
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.List;
+
+import static com.alibaba.fastjson2.JSONWriter.Feature.*;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class PrettyFormatTest {
+    @Test
+    public void testArray() {
+        JSONArray array = JSONArray.of(1, 2, 3);
+        assertEquals("[1,2,3]", array.toString());
+        assertEquals("[1,2,3]", array.toString(OptimizedForAscii));
+
+        assertEquals(
+                "[\n" +
+                        "\t1,\n" +
+                        "\t2,\n" +
+                        "\t3\n" +
+                        "]",
+                array
+                        .toString(PrettyFormat));
+        assertEquals(
+                "[\n" +
+                        "\t1,\n" +
+                        "\t2,\n" +
+                        "\t3\n" +
+                        "]",
+                array
+                        .toString(PrettyFormat, OptimizedForAscii));
+    }
+
+    @Test
+    public void test() {
+        JSONObject jsonObject = JSONObject.of("id", 123, "value", "abc");
+
+        assertEquals("{\"id\":123,\"value\":\"abc\"}", jsonObject.toString());
+        assertEquals("{\"id\":123,\"value\":\"abc\"}", jsonObject.toString(OptimizedForAscii));
+
+        assertEquals(
+                "{\n" +
+                        "\t\"id\":123,\n" +
+                        "\t\"value\":\"abc\"\n" +
+                        "}",
+                jsonObject
+                        .toString(PrettyFormat));
+        assertEquals(
+                "{\n" +
+                        "\t\"id\":123,\n" +
+                        "\t\"value\":\"abc\"\n" +
+                        "}",
+                jsonObject
+                        .toString(PrettyFormat, OptimizedForAscii));
+        assertEquals(
+                "{\n" +
+                        "    \"id\":123,\n" +
+                        "    \"value\":\"abc\"\n" +
+                        "}",
+                jsonObject
+                        .toString(PrettyFormatWith4Space));
+        assertEquals(
+                "{\n" +
+                        "    \"id\":123,\n" +
+                        "    \"value\":\"abc\"\n" +
+                        "}",
+                jsonObject
+                        .toString(PrettyFormatWith4Space, OptimizedForAscii));
+
+        assertEquals(
+                "{\n" +
+                        "  \"id\":123,\n" +
+                        "  \"value\":\"abc\"\n" +
+                        "}",
+                jsonObject
+                        .toString(PrettyFormatWith2Space));
+        assertEquals(
+                "{\n" +
+                        "  \"id\":123,\n" +
+                        "  \"value\":\"abc\"\n" +
+                        "}",
+                jsonObject
+                        .toString(PrettyFormatWith2Space, OptimizedForAscii));
+    }
+
+    @Test
+    public void test1() {
+        Bean1 bean = new Bean1();
+        bean.setId(123);
+        bean.setValue("abc");
+
+        assertEquals(
+                "{\n" +
+                        "\t\"id\":123,\n" +
+                        "\t\"value\":\"abc\"\n" +
+                        "}",
+                JSON.toJSONString(bean, PrettyFormat));
+        assertEquals(
+                "{\n" +
+                        "\t\"id\":123,\n" +
+                        "\t\"value\":\"abc\"\n" +
+                        "}",
+                JSON.toJSONString(bean, PrettyFormat, OptimizedForAscii));
+        assertEquals(
+                "{\n" +
+                        "    \"id\":123,\n" +
+                        "    \"value\":\"abc\"\n" +
+                        "}",
+                JSON.toJSONString(bean, PrettyFormatWith4Space));
+        assertEquals(
+                "{\n" +
+                        "    \"id\":123,\n" +
+                        "    \"value\":\"abc\"\n" +
+                        "}",
+                JSON.toJSONString(bean, PrettyFormatWith4Space, OptimizedForAscii));
+
+        Bean1[] array = new Bean1[] {bean};
+        assertEquals(
+                "[\n" +
+                        "\t{\n" +
+                        "\t\t\"id\":123,\n" +
+                        "\t\t\"value\":\"abc\"\n" +
+                        "\t}\n" +
+                        "]",
+                JSON.toJSONString(array, PrettyFormat));
+        assertEquals(
+                "[\n" +
+                        "    {\n" +
+                        "        \"id\":123,\n" +
+                        "        \"value\":\"abc\"\n" +
+                        "    }\n" +
+                        "]",
+                JSON.toJSONString(array, PrettyFormatWith4Space));
+    }
+
+    @Data
+    private static class Bean1 {
+        private int id;
+        private String value;
+    }
+
+    @Test
+    public void test2() {
+        Bean2 bean = new Bean2();
+        bean.setId(123);
+        bean.setValue("abc");
+
+        assertEquals(
+                "{\n" +
+                        "\t\"id\":123,\n" +
+                        "\t\"value\":\"abc\"\n" +
+                        "}",
+                JSON.toJSONString(bean, PrettyFormat));
+        assertEquals(
+                "{\n" +
+                        "\t\"id\":123,\n" +
+                        "\t\"value\":\"abc\"\n" +
+                        "}",
+                JSON.toJSONString(bean, PrettyFormat, OptimizedForAscii));
+        assertEquals(
+                "{\n" +
+                        "    \"id\":123,\n" +
+                        "    \"value\":\"abc\"\n" +
+                        "}",
+                JSON.toJSONString(bean, PrettyFormatWith4Space));
+        assertEquals(
+                "{\n" +
+                        "    \"id\":123,\n" +
+                        "    \"value\":\"abc\"\n" +
+                        "}",
+                JSON.toJSONString(bean, PrettyFormatWith4Space, OptimizedForAscii));
+
+        Bean2[] array = new Bean2[] {bean};
+        assertEquals(
+                "[\n" +
+                        "\t{\n" +
+                        "\t\t\"id\":123,\n" +
+                        "\t\t\"value\":\"abc\"\n" +
+                        "\t}\n" +
+                        "]",
+                JSON.toJSONString(array, PrettyFormat));
+        assertEquals(
+                "[\n" +
+                        "    {\n" +
+                        "        \"id\":123,\n" +
+                        "        \"value\":\"abc\"\n" +
+                        "    }\n" +
+                        "]",
+                JSON.toJSONString(array, PrettyFormatWith4Space));
+    }
+
+    @Data
+    public static class Bean2 {
+        private int id;
+        private String value;
+    }
+
+    @Test
+    public void test3() {
+        Bean3 bean = new Bean3();
+        bean.values.add("a01");
+        bean.values.add("a02");
+
+        assertEquals(
+                "{\n" +
+                        "\t\"values\":[\n" +
+                        "\t\t\"a01\",\n" +
+                        "\t\t\"a02\"\n" +
+                        "\t]\n" +
+                        "}",
+                JSON.toJSONString(bean, PrettyFormat));
+        assertEquals(
+                "{\n" +
+                        "\t\"values\":[\n" +
+                        "\t\t\"a01\",\n" +
+                        "\t\t\"a02\"\n" +
+                        "\t]\n" +
+                        "}",
+                JSON.toJSONString(bean, PrettyFormat, OptimizedForAscii));
+    }
+
+    public static class Bean3 {
+        public List<String> values = new ArrayList<>();
+    }
+
+    @Test
+    public void ofPrettyUTF16() {
+        JSONWriter jsonWriter = JSONWriter.ofUTF16();
+        jsonWriter = JSONWriter.ofPretty(jsonWriter);
+        jsonWriter = JSONWriter.ofPretty(jsonWriter);
+        jsonWriter.startObject();
+        jsonWriter.writeNameValue("id", 123);
+        jsonWriter.endObject();
+        jsonWriter.close();
+
+        assertEquals("{\n" +
+                "\t\"id\"123\n" +
+                "}", jsonWriter.toString());
+
+        jsonWriter.incrementIndent();
+        assertEquals(1, jsonWriter.level());
+        jsonWriter.println();
+        assertEquals("{\n" +
+                "\t\"id\"123\n" +
+                "}\n\t", jsonWriter.toString());
+        jsonWriter.decrementIdent();
+        assertEquals(0, jsonWriter.level());
+    }
+
+    @Test
+    public void ofPrettyUTF8() {
+        JSONWriter jsonWriter = JSONWriter.ofUTF8();
+        jsonWriter = JSONWriter.ofPretty(jsonWriter);
+        jsonWriter = JSONWriter.ofPretty(jsonWriter);
+        jsonWriter.startObject();
+        jsonWriter.writeNameValue("id", 123);
+        jsonWriter.endObject();
+        jsonWriter.close();
+
+        assertEquals("{\n" +
+                "\t\"id\"123\n" +
+                "}", jsonWriter.toString());
+
+        jsonWriter.incrementIndent();
+        assertEquals(1, jsonWriter.level());
+        jsonWriter.println();
+        assertEquals("{\n" +
+                "\t\"id\"123\n" +
+                "}\n\t", jsonWriter.toString());
+        jsonWriter.decrementIdent();
+        assertEquals(0, jsonWriter.level());
+    }
+
+    @Test
+    public void testJSONObject() {
+        {
+            JSONObject object = JSONObject.of(
+                    "f0", null,
+                    "f1", 101,
+                    "f2", 102L,
+                    "f3", new BigDecimal("103"),
+                    "f4", new JSONObject(),
+                    "f5", new JSONArray(),
+                    "f6", (short) 106,
+                    "f7", true
+            );
+            {
+                JSONWriter jsonWriter = JSONWriter.ofUTF16(PrettyFormat);
+                jsonWriter.write(object);
+                assertEquals("{\n" +
+                        "\t\"f1\":101,\n" +
+                        "\t\"f2\":102,\n" +
+                        "\t\"f3\":103,\n" +
+                        "\t\"f4\":{},\n" +
+                        "\t\"f5\":[],\n" +
+                        "\t\"f6\":106,\n" +
+                        "\t\"f7\":true\n" +
+                        "}", jsonWriter.toString());
+            }
+            {
+                JSONWriter jsonWriter = JSONWriter.ofUTF8(PrettyFormat);
+                jsonWriter.write(object);
+                assertEquals("{\n" +
+                        "\t\"f1\":101,\n" +
+                        "\t\"f2\":102,\n" +
+                        "\t\"f3\":103,\n" +
+                        "\t\"f4\":{},\n" +
+                        "\t\"f5\":[],\n" +
+                        "\t\"f6\":106,\n" +
+                        "\t\"f7\":true\n" +
+                        "}", jsonWriter.toString());
+            }
+            {
+                JSONWriter jsonWriter = JSONWriter.of(PrettyFormat, WriteNulls);
+                jsonWriter.write(object);
+                assertEquals("{\n" +
+                        "\t\"f0\":null,\n" +
+                        "\t\"f1\":101,\n" +
+                        "\t\"f2\":102,\n" +
+                        "\t\"f3\":103,\n" +
+                        "\t\"f4\":{},\n" +
+                        "\t\"f5\":[],\n" +
+                        "\t\"f6\":106,\n" +
+                        "\t\"f7\":true\n" +
+                        "}", jsonWriter.toString());
+            }
+        }
+        {
+            JSONWriter jsonWriter = JSONWriter.of(PrettyFormat);
+            jsonWriter.write(JSONObject.of());
+            assertEquals("{}", jsonWriter.toString());
+        }
+    }
+}
diff --git a/core/src/test/java/com/alibaba/fastjson2/issues_1500/Issue1591.java b/core/src/test/java/com/alibaba/fastjson2/issues_1500/Issue1591.java
index a8e11e3f8..e46b4e3a4 100644
--- a/core/src/test/java/com/alibaba/fastjson2/issues_1500/Issue1591.java
+++ b/core/src/test/java/com/alibaba/fastjson2/issues_1500/Issue1591.java
@@ -25,9 +25,16 @@ public class Issue1591 {
             );
         }
 
-        String str = JSON.toJSONString(array, JSONWriter.Feature.PrettyFormat);
-        assertNotNull(str);
-        assertEquals(array, JSON.parseArray(str));
+        {
+            String str = JSON.toJSONString(array, JSONWriter.Feature.PrettyFormat);
+            assertNotNull(str);
+            assertEquals(array, JSON.parseArray(str));
+        }
+        {
+            String str = JSON.toJSONString(array, JSONWriter.Feature.PrettyFormat, JSONWriter.Feature.OptimizedForAscii);
+            assertNotNull(str);
+            assertEquals(array, JSON.parseArray(str));
+        }
     }
 
     @Test
@@ -49,8 +56,15 @@ public class Issue1591 {
             );
         }
 
-        String str = JSON.toJSONString(array, JSONWriter.Feature.PrettyFormat);
-        assertNotNull(str);
-        assertEquals(array, JSON.parseArray(str));
+        {
+            String str = JSON.toJSONString(array, JSONWriter.Feature.PrettyFormat);
+            assertNotNull(str);
+            assertEquals(array, JSON.parseArray(str));
+        }
+        {
+            String str = JSON.toJSONString(array, JSONWriter.Feature.PrettyFormat, JSONWriter.Feature.OptimizedForAscii);
+            assertNotNull(str);
+            assertEquals(array, JSON.parseArray(str));
+        }
     }
 }
diff --git a/core/src/test/java/com/alibaba/fastjson2/write/PrettyTest.java b/core/src/test/java/com/alibaba/fastjson2/write/PrettyTest.java
index e47e9f00a..462a7e46b 100644
--- a/core/src/test/java/com/alibaba/fastjson2/write/PrettyTest.java
+++ b/core/src/test/java/com/alibaba/fastjson2/write/PrettyTest.java
@@ -182,9 +182,7 @@ public class PrettyTest {
         JSONWriter jw = JSONWriter.ofPretty();
         jw.writeAny(Collections.emptyMap());
 
-        assertEquals("{\n" +
-                        "\t\n" +
-                        "}",
+        assertEquals("{}",
                 jw.toString());
     }
 
@@ -201,12 +199,8 @@ public class PrettyTest {
         assertEquals("{\n" +
                         "\t\"id\":1001,\n" +
                         "\t\"name\":\"DataWorks\",\n" +
-                        "\t\"array\":[\n" +
-                        "\t\t\n" +
-                        "\t],\n" +
-                        "\t\"object\":{\n" +
-                        "\t\t\n" +
-                        "\t}\n" +
+                        "\t\"array\":[],\n" +
+                        "\t\"object\":{}\n" +
                         "}",
                 jw.toString());
     }
@@ -215,7 +209,7 @@ public class PrettyTest {
     public void test_pretty_array() {
         JSONWriter jw = JSONWriter.ofPretty();
         jw.writeAny(Collections.emptyList());
-        assertEquals("[\n\t\n]", jw.toString());
+        assertEquals("[]", jw.toString());
     }
 
     @Test