content: package com.alibaba.fastjson2;
  
  import com.alibaba.fastjson2.util.IOUtils;
  import com.alibaba.fastjson2.util.NumberUtils;
  import com.alibaba.fastjson2.util.StringUtils;
  import com.alibaba.fastjson2.writer.ObjectWriter;
  
  import java.io.IOException;
  import java.io.OutputStream;
  import java.math.BigDecimal;
  import java.math.BigInteger;
  import java.nio.charset.Charset;
  import java.nio.charset.StandardCharsets;
  import java.text.DecimalFormat;
  import java.time.*;
  import java.util.Arrays;
  import java.util.List;
  import java.util.Map;
  import java.util.UUID;
  
  import static com.alibaba.fastjson2.JSONFactory.*;
  import static com.alibaba.fastjson2.JSONWriter.Feature.*;
  import static com.alibaba.fastjson2.util.IOUtils.*;
  import static com.alibaba.fastjson2.util.JDKUtils.*;
  import static com.alibaba.fastjson2.util.TypeUtils.isInt64;
  
  final class JSONWriterUTF8
          extends JSONWriter {
      static final long REF;
      static final short QUOTE2_COLON, QUOTE_COLON;
  
      static {
          byte[] chars = {'{', '"', '$', 'r', 'e', 'f', '"', ':'};
          REF = UNSAFE.getLong(chars, ARRAY_CHAR_BASE_OFFSET);
          QUOTE2_COLON = UNSAFE.getShort(chars, ARRAY_CHAR_BASE_OFFSET + 6);
          chars[6] = '\'';
          QUOTE_COLON = UNSAFE.getShort(chars, ARRAY_CHAR_BASE_OFFSET + 6);
      }
  
      final CacheItem cacheItem;
      byte[] bytes;
      private final long byteVectorQuote;
  
      JSONWriterUTF8(Context ctx) {
          super(ctx, null, false, StandardCharsets.UTF_8);
          int cacheIndex = System.identityHashCode(Thread.currentThread()) & (CACHE_ITEMS.length - 1);
          cacheItem = CACHE_ITEMS[cacheIndex];
          byte[] bytes = BYTES_UPDATER.getAndSet(cacheItem, null);
          if (bytes == null) {
              bytes = new byte[8192];
          }
          this.bytes = bytes;
          this.byteVectorQuote = this.useSingleQuote ? ~0x2727_2727_2727_2727L : ~0x2222_2222_2222_2222L;
      }
  
      public final void writeNull() {
          int off = this.off;
          byte[] bytes = this.bytes;
          if (off + 4 > bytes.length) {
              bytes = grow(off + 4);
          }
          IOUtils.putNULL(bytes, off);
          this.off = off + 4;
      }
  
      @Override
      public final void writeReference(String path) {
          this.lastReference = path;
          int off = this.off;
          byte[] bytes = this.bytes;
          if (off + 8 > bytes.length) {
              bytes = grow(off + 8);
          }
          UNSAFE.putLong(bytes, ARRAY_BYTE_BASE_OFFSET + off, REF);
          this.off = off + 8;
          writeString(path);
          writeRaw((byte) '}');
      }
  
      @Override
      public final void writeBase64(byte[] value) {
          int charsLen = ((value.length - 1) / 3 + 1) << 2; // base64 character count
  
          int off = this.off;
          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.
  
          for (int s = 0; s < eLen; ) {
              // Copy next three bytes into lower 24 bits of int, paying attension to sign.
              int i = (value[s++] & 0xff) << 16 | (value[s++] & 0xff) << 8 | (value[s++] & 0xff);
  
              // Encode the int into four chars
              bytes[off] = (byte) CA[(i >>> 18) & 0x3f];
              bytes[off + 1] = (byte) CA[(i >>> 12) & 0x3f];
              bytes[off + 2] = (byte) CA[(i >>> 6) & 0x3f];
              bytes[off + 3] = (byte) CA[i & 0x3f];
              off += 4;
          }
  
          // Pad and encode last bits if source isn't even 24 bits.
          int left = value.length - eLen; // 0 - 2.
          if (left > 0) {
              // Prepare the int
              int i = ((value[eLen] & 0xff) << 10) | (left == 2 ? ((value[value.length - 1] & 0xff) << 2) : 0);
  
              // Set last four chars
              bytes[off] = (byte) CA[i >> 12];
              bytes[off + 1] = (byte) CA[(i >>> 6) & 0x3f];
              bytes[off + 2] = left == 2 ? (byte) CA[i & 0x3f] : (byte) '=';
              bytes[off + 3] = '=';
              off += 4;
          }
  
          bytes[off] = (byte) quote;
          this.off = off + 1;
      }
  
      @Override
      public final void writeHex(byte[] values) {
          if (values == null) {
              writeNull();
              return;
          }
  
          int charsLen = values.length * 2 + 3;
  
          int off = this.off;
          int minCapacity = off + charsLen + 2;
          byte[] bytes = this.bytes;
          if (minCapacity > bytes.length) {
              bytes = grow(minCapacity);
          }
          putShortLE(bytes, off, (short) ('x' | ('\'' << 8)));
          off += 2;
  
          for (int i = 0; i < values.length; i++) {
              putShortLE(bytes, off, hex2U(values[i]));
              off += 2;
          }
  
          bytes[off] = '\'';
          this.off = off + 1;
      }
  
      @Override
      public final void close() {
          byte[] bytes = this.bytes;
          if (bytes.length > CACHE_THRESHOLD) {
              return;
          }
  
          BYTES_UPDATER.lazySet(cacheItem, bytes);
      }
  
      public final int size() {
          return off;
      }
  
      @Override
      public final byte[] getBytes() {
          return Arrays.copyOf(bytes, off);
      }
  
      @Override
      public final byte[] getBytes(Charset charset) {
          if (charset == StandardCharsets.UTF_8) {
              return Arrays.copyOf(bytes, off);
          }
  
          return toString()
                  .getBytes(charset);
      }
  
      @Override
      public final int flushTo(OutputStream to) throws IOException {
          int off = this.off;
          if (off > 0) {
              to.write(bytes, 0, off);
              this.off = 0;
          }
          return off;
      }
  
      @Override
      protected final void write0(char c) {
          int off = this.off;
          if (off == bytes.length) {
              grow0(off + 1);
          }
          bytes[off] = (byte) c;
          this.off = off + 1;
      }
  
      @Override
      public final void writeColon() {
          int off = this.off;
          grow1(off)[off] = ':';
          this.off = off + 1;
      }
  
      @Override
      public final void startObject() {
          if (++level > context.maxLevel) {
              overflowLevel();
          }
  
          startObject = true;
  
          int off = this.off;
          int minCapacity = off + 3 + pretty * level;
          byte[] bytes = this.bytes;
          if (minCapacity > bytes.length) {
              bytes = grow(minCapacity);
          }
          bytes[off++] = '{';
  
          if (pretty != PRETTY_NON) {
              off = indent(bytes, off);
          }
          this.off = off;
      }
  
      @Override
      public final void endObject() {
          level--;
          int off = this.off;
          int minCapacity = off + 1 + (pretty == 0 ? 0 : pretty * level + 1);
          byte[] bytes = this.bytes;
          if (minCapacity > bytes.length) {
              bytes = grow(minCapacity);
          }
          if (pretty != PRETTY_NON) {
              off = indent(bytes, off);
          }
  
          bytes[off] = '}';
          this.off = off + 1;
          startObject = false;
      }
  
      @Override
      public final void writeComma() {
          startObject = false;
          int off = this.off;
          int minCapacity = off + 2 + pretty * level;
          byte[] bytes = this.bytes;
          if (minCapacity > bytes.length) {
              bytes = grow(minCapacity);
          }
          bytes[off++] = ',';
          if (pretty != PRETTY_NON) {
              off = indent(bytes, off);
          }
          this.off = off;
      }
  
      @Override
      public final void startArray() {
          if (++level > context.maxLevel) {
              overflowLevel();
          }
  
          int off = this.off;
          int minCapacity = off + 3 + pretty * level;
          byte[] bytes = this.bytes;
          if (minCapacity > bytes.length) {
              bytes = grow(minCapacity);
          }
          bytes[off++] = '[';
          if (pretty != PRETTY_NON) {
              off = indent(bytes, off);
          }
          this.off = off;
      }
  
      @Override
      public final void endArray() {
          level--;
          int off = this.off;
          int minCapacity = off + 1 + (pretty == 0 ? 0 : pretty * level + 1);
          byte[] bytes = this.bytes;
          if (minCapacity > bytes.length) {
              bytes = grow(minCapacity);
          }
          if (pretty != PRETTY_NON) {
              off = indent(bytes, off);
          }
          bytes[off] = ']';
          this.off = off + 1;
          startObject = false;
      }
  
      public final void writeString(String[] strings) {
          if (pretty != PRETTY_NON || strings == null) {
              super.writeString(strings);
              return;
          }
          int off = this.off;
          byte[] bytes = grow1(off);
          bytes[off] = '[';
          this.off = off + 1;
  
          for (int i = 0; i < strings.length; i++) {
              if (i != 0) {
                  off = this.off;
                  bytes = grow1(off);
                  bytes[off] = ',';
                  this.off = off + 1;
              }
  
              writeString(
                      strings[i]);
          }
  
          off = this.off;
          bytes = grow1(off);
          bytes[off] = ']';
          this.off = off + 1;
      }
  
      public final void writeString(List<String> list) {
          if (pretty != PRETTY_NON) {
              super.writeString(list);
              return;
          }
          int off = this.off;
          byte[] bytes = grow1(off);
          bytes[off] = (byte) '[';
          this.off = off + 1;
  
          for (int i = 0, size = list.size(); i < size; i++) {
              if (i != 0) {
                  off = this.off;
                  bytes = grow1(off);
                  bytes[off] = ',';
                  this.off = off + 1;
              }
  
              String str = list.get(i);
              if (str == null) {
                  writeStringNull();
              } else {
                  if (STRING_VALUE != null) {
                      byte[] value = STRING_VALUE.apply(str);
                      if (STRING_CODER.applyAsInt(str) == 0) {
                          writeStringLatin1(value);
                      } else {
                          writeStringUTF16(value);
                      }
                  } else {
                      writeStringJDK8(str);
                  }
              }
          }
  
          off = this.off;
          bytes = grow1(off);
          bytes[off] = ']';
          this.off = off + 1;
      }
  
      @Override
      public final void writeString(boolean value) {
          byte quote = (byte) this.quote;
          bytes[off++] = quote;
          writeBool(value);
          bytes[off++] = quote;
      }
  
      @Override
      public final void writeString(byte value) {
          boolean writeAsString = (context.features & WriteNonStringValueAsString.mask) == 0;
          if (writeAsString) {
              writeQuote();
          }
          writeInt8(value);
          if (writeAsString) {
              writeQuote();
          }
      }
  
      @Override
      public final void writeString(short value) {
          boolean writeAsString = (context.features & WriteNonStringValueAsString.mask) == 0;
          if (writeAsString) {
              writeQuote();
          }
          writeInt16(value);
          if (writeAsString) {
              writeQuote();
          }
      }
  
      @Override
      public final void writeString(int value) {
          boolean writeAsString = (context.features & WriteNonStringValueAsString.mask) == 0;
          if (writeAsString) {
              writeQuote();
          }
          writeInt32(value);
          if (writeAsString) {
              writeQuote();
          }
      }
  
      @Override
      public final void writeString(long value) {
          boolean writeAsString = (context.features & (WriteNonStringValueAsString.mask | WriteLongAsString.mask)) == 0;
          if (writeAsString) {
              writeQuote();
          }
          writeInt64(value);
          if (writeAsString) {
              writeQuote();
          }
      }
  
      private void writeQuote() {
          writeRaw((byte) quote);
      }
  
      @Override
      public void writeString(String str) {
          if (str == null) {
              writeStringNull();
              return;
          }
  
          if (STRING_VALUE != null) {
              byte[] value = STRING_VALUE.apply(str);
              if (STRING_CODER.applyAsInt(str) == 0) {
                  writeStringLatin1(value);
              } else {
                  writeStringUTF16(value);
              }
          } else {
              writeStringJDK8(str);
          }
      }
  
      private void writeStringJDK8(String str) {
          char[] chars = getCharArray(str);
  
          boolean browserSecure = (context.features & BrowserSecure.mask) != 0;
          boolean escapeNoneAscii = (context.features & EscapeNoneAscii.mask) != 0;
  
          int off = this.off;
          // ensureCapacity
          int minCapacity = off
                  + chars.length * 3 // utf8 3 bytes
                  + 2;
  
          if (escapeNoneAscii || browserSecure) {
              minCapacity += chars.length * 3;
          }
  
          byte[] bytes = this.bytes;
          if (minCapacity > bytes.length) {
              bytes = grow(minCapacity);
          }
  
          bytes[off++] = (byte) quote;
  
          int i = 0;
          for (; i < chars.length; i++) {
              char c0 = chars[i];
              if (c0 == quote
                      || c0 == '\\'
                      || c0 < ' '
                      || c0 > 0x007F
                      || (browserSecure
                      && (c0 == '<' || c0 == '>' || c0 == '(' || c0 == ')'))
              ) {
                  break;
              }
  
              bytes[off++] = (byte) c0;
          }
  
          if (i == chars.length) {
              bytes[off] = (byte) quote;
              this.off = off + 1;
              return;
          }
  
          this.off = off;
          if (i < chars.length) {
              writeStringEscapedRest(chars, chars.length, browserSecure, escapeNoneAscii, i);
          }
  
          this.bytes[this.off++] = (byte) quote;
      }
  
      public final void writeStringLatin1(byte[] value) {
          if ((context.features & MASK_BROWSER_SECURE) != 0) {
              writeStringLatin1BrowserSecure(value);
              return;
          }
  
          byte quote = (byte) this.quote;
          if (StringUtils.escaped(value, quote, byteVectorQuote)) {
              writeStringEscaped(value);
              return;
          }
  
          int off = this.off;
          int minCapacity = off + value.length + 2;
          byte[] bytes = this.bytes;
          if (minCapacity > bytes.length) {
              bytes = grow(minCapacity);
          }
          this.off = StringUtils.writeLatin1(bytes, off, value, quote);
      }
  
      protected final void writeStringLatin1BrowserSecure(byte[] value) {
          final byte quote = (byte) this.quote;
          int i = 0;
          for (; i < value.length; i++) {
              byte c = value[i];
              if (c == quote || c == '\\' || c < ' ' || c == '<' || c == '>' || c == '(' || c == ')') {
                  break;
              }
          }
  
          int off = this.off;
          if (i == value.length) {
              int minCapacity = off + value.length + 2;
              byte[] bytes = this.bytes;
              if (minCapacity > bytes.length) {
                  bytes = grow(minCapacity);
              }
              bytes[off] = quote;
              System.arraycopy(value, 0, bytes, off + 1, value.length);
              off += value.length + 1;
              bytes[off] = quote;
              this.off = off + 1;
              return;
          }
          writeStringEscaped(value);
      }
  
      public final void writeStringUTF16(byte[] value) {
          if (value == null) {
              writeStringNull();
              return;
          }
  
          int off = this.off;
          byte[] bytes = this.bytes;
          int minCapacity = off + value.length * 6 + 2;
          if (minCapacity > bytes.length) {
              bytes = grow(minCapacity);
          }
          this.off = StringUtils.writeUTF16(bytes, off, value, (byte) quote, context.features);
      }
  
      public final void writeString(final char[] chars) {
          if (chars == null) {
              writeStringNull();
              return;
          }
  
          boolean browserSecure = (context.features & BrowserSecure.mask) != 0;
          boolean escapeNoneAscii = (context.features & EscapeNoneAscii.mask) != 0;
  
          int off = this.off;
          int minCapacity = off
                  + chars.length * 3 /* utf8 3 bytes */
                  + 2;
  
          if (escapeNoneAscii || browserSecure) {
              minCapacity += chars.length * 3;
          }
  
          byte[] bytes = this.bytes;
          if (minCapacity > bytes.length) {
              bytes = grow(minCapacity);
          }
          bytes[off++] = (byte) quote;
  
          int i = 0;
          for (; i < chars.length; i++) {
              char c = chars[i];
              if (c == quote
                      || c == '\\'
                      || c < ' '
                      || c > 0x007F
                      || (browserSecure
                      && (c == '<' || c == '>' || c == '(' || c == ')'))
              ) {
                  break;
              }
              bytes[off++] = (byte) c;
          }
  
          this.off = off;
          if (i < chars.length) {
              writeStringEscapedRest(chars, chars.length, browserSecure, escapeNoneAscii, i);
          }
  
          this.bytes[this.off++] = (byte) quote;
      }
  
      public final void writeString(final char[] chars, int stroff, int strlen) {
          if (chars == null) {
              if (isEnabled(NullAsDefaultValue.mask | WriteNullStringAsEmpty.mask)) {
                  writeString("");
                  return;
              }
  
              writeNull();
              return;
          }
  
          int end = stroff + strlen;
  
          boolean browserSecure = (context.features & BrowserSecure.mask) != 0;
          boolean escapeNoneAscii = (context.features & EscapeNoneAscii.mask) != 0;
  
          int off = this.off;
          // ensureCapacity
          int minCapacity = off
                  + strlen * 3 // utf8 3 bytes
                  + 2;
  
          if (escapeNoneAscii || browserSecure) {
              minCapacity += strlen * 3;
          }
  
          byte[] bytes = this.bytes;
          if (minCapacity > bytes.length) {
              bytes = grow(minCapacity);
          }
          bytes[off++] = (byte) quote;
  
          int i = stroff;
          for (; i < end; i++) {
              char c0 = chars[i];
              if (c0 == quote
                      || c0 == '\\'
                      || c0 < ' '
                      || c0 > 0x007F
                      || (browserSecure
                      && (c0 == '<' || c0 == '>' || c0 == '(' || c0 == ')'))
              ) {
                  break;
              }
              bytes[off++] = (byte) c0;
          }
          this.off = off;
          if (i < end) {
              writeStringEscapedRest(chars, end, browserSecure, escapeNoneAscii, i);
          }
  
          this.bytes[this.off++] = (byte) quote;
      }
  
      protected final void writeStringEscaped(byte[] values) {
          int minCapacity = off + values.length * 6 + 2;
          byte[] bytes = this.bytes;
          if (minCapacity > bytes.length) {
              bytes = grow(minCapacity);
          }
          this.off = StringUtils.writeLatin1Escaped(bytes, off, values, (byte) quote, context.features);
      }
  
      protected final void writeStringEscapedRest(
              char[] chars,
              int end,
              boolean browserSecure,
              boolean escapeNoneAscii,
              int i
      ) {
          int rest = chars.length - i;
          int minCapacity = off + rest * 6 + 2;
          byte[] bytes = this.bytes;
          if (minCapacity > bytes.length) {
              bytes = grow(minCapacity);
          }
  
          int off = this.off;
          for (; i < end; ++i) { // ascii none special fast write
              char ch = chars[i];
              if (ch <= 0x007F) {
                  switch (ch) {
                      case '\\':
                      case '\n':
                      case '\r':
                      case '\f':
                      case '\b':
                      case '\t':
                          StringUtils.writeEscapedChar(bytes, off, ch);
                          off += 2;
                          break;
                      case 0:
                      case 1:
                      case 2:
                      case 3:
                      case 4:
                      case 5:
                      case 6:
                      case 7:
                      case 11:
                      case 14:
                      case 15:
                      case 16:
                      case 17:
                      case 18:
                      case 19:
                      case 20:
                      case 21:
                      case 22:
                      case 23:
                      case 24:
                      case 25:
                      case 26:
                      case 27:
                      case 28:
                      case 29:
                      case 30:
                      case 31:
                          StringUtils.writeU4Hex2(bytes, off, ch);
                          off += 6;
                          break;
                      case '<':
                      case '>':
                      case '(':
                      case ')':
                          if (browserSecure) {
                              StringUtils.writeU4HexU(bytes, off, ch);
                              off += 6;
                          } else {
                              bytes[off++] = (byte) ch;
                          }
                          break;
                      default:
                          if (ch == quote) {
                              bytes[off] = '\\';
                              bytes[off + 1] = (byte) quote;
                              off += 2;
                          } else {
                              bytes[off++] = (byte) ch;
                          }
                          break;
                  }
              } else if (escapeNoneAscii) {
                  StringUtils.writeU4HexU(bytes, off, ch);
                  off += 6;
              } else if (ch >= '\uD800' && ch < ('\uDFFF' + 1)) { //  //Character.isSurrogate(c)
                  final int uc;
                  if (ch < '\uDBFF' + 1) { // Character.isHighSurrogate(c)
                      if (chars.length - i < 2) {
                          uc = -1;
                      } else {
                          char d = chars[i + 1];
                          // d >= '\uDC00' && d < ('\uDFFF' + 1)
                          if (d >= '\uDC00' && d < ('\uDFFF' + 1)) { // Character.isLowSurrogate(d)
                              uc = ((ch << 10) + d) + (0x010000 - ('\uD800' << 10) - '\uDC00'); // Character.toCodePoint(c, d)
                          } else {
  //                            throw new JSONException("encodeUTF8 error", new MalformedInputException(1));
                              bytes[off++] = '?';
                              continue;
                          }
                      }
                  } else {
                      //
                      // Character.isLowSurrogate(c)
                      bytes[off++] = '?';
                      continue;
  //                        throw new JSONException("encodeUTF8 error", new MalformedInputException(1));
                  }
  
                  if (uc < 0) {
                      bytes[off++] = '?';
                  } else {
                      bytes[off] = (byte) (0xf0 | ((uc >> 18)));
                      bytes[off + 1] = (byte) (0x80 | ((uc >> 12) & 0x3f));
                      bytes[off + 2] = (byte) (0x80 | ((uc >> 6) & 0x3f));
                      bytes[off + 3] = (byte) (0x80 | (uc & 0x3f));
                      off += 4;
                      i++; // 2 chars
                  }
              } else if (ch > 0x07FF) {
                  bytes[off] = (byte) (0xE0 | ((ch >> 12) & 0x0F));
                  bytes[off + 1] = (byte) (0x80 | ((ch >> 6) & 0x3F));
                  bytes[off + 2] = (byte) (0x80 | (ch & 0x3F));
                  off += 3;
              } else {
                  bytes[off] = (byte) (0xC0 | ((ch >> 6) & 0x1F));
                  bytes[off + 1] = (byte) (0x80 | (ch & 0x3F));
                  off += 2;
              }
          }
          this.off = off;
      }
  
      @Override
      public final void writeString(char[] chars, int offset, int len, boolean quoted) {
          boolean escapeNoneAscii = (context.features & EscapeNoneAscii.mask) != 0;
  
          // ensureCapacity
          int minCapacity = off
                  + chars.length * 3 // utf8 3 bytes
                  + 2;
  
          if (escapeNoneAscii) {
              minCapacity += len * 3;
          }
  
          byte[] bytes = this.bytes;
          if (minCapacity > bytes.length) {
              bytes = grow(minCapacity);
          }
          int off = this.off;
          if (quoted) {
              bytes[off++] = (byte) quote;
          }
  
          int end = offset + len;
  
          int i = offset;
          for (; i < end; i++) {
              char c0 = chars[i];
              if (c0 == quote
                      || c0 == '\\'
                      || c0 < ' '
                      || c0 > 0x007F) {
                  break;
              }
              bytes[off++] = (byte) c0;
          }
  
          if (i == end) {
              if (quoted) {
                  bytes[off++] = (byte) quote;
              }
              this.off = off;
              return;
          }
  
          for (; i < end; ++i) { // ascii none special fast write
              char ch = chars[i];
              if (ch <= 0x007F) {
                  switch (ch) {
                      case '\\':
                      case '\n':
                      case '\r':
                      case '\f':
                      case '\b':
                      case '\t':
                          StringUtils.writeEscapedChar(bytes, off, ch);
                          off += 2;
                          break;
                      case 0:
                      case 1:
                      case 2:
                      case 3:
                      case 4:
                      case 5:
                      case 6:
                      case 7:
                      case 11:
                      case 14:
                      case 15:
                      case 16:
                      case 17:
                      case 18:
                      case 19:
                      case 20:
                      case 21:
                      case 22:
                      case 23:
                      case 24:
                      case 25:
                      case 26:
                      case 27:
                      case 28:
                      case 29:
                      case 30:
                      case 31:
                          StringUtils.writeU4Hex2(bytes, off, ch);
                          off += 6;
                          break;
                      default:
                          if (ch == quote) {
                              bytes[off] = '\\';
                              bytes[off + 1] = (byte) quote;
                              off += 2;
                          } else {
                              bytes[off++] = (byte) ch;
                          }
                          break;
                  }
              } else if (escapeNoneAscii) {
                  StringUtils.writeU4HexU(bytes, off, ch);
                  off += 6;
              } else if (ch >= '\uD800' && ch < ('\uDFFF' + 1)) { //  //Character.isSurrogate(c)
                  final int uc;
                  if (ch < '\uDBFF' + 1) { // Character.isHighSurrogate(c)
                      if (chars.length - i < 2) {
                          uc = -1;
                      } else {
                          char d = chars[i + 1];
                          // d >= '\uDC00' && d < ('\uDFFF' + 1)
                          if (d >= '\uDC00' && d < ('\uDFFF' + 1)) { // Character.isLowSurrogate(d)
                              uc = ((ch << 10) + d) + (0x010000 - ('\uD800' << 10) - '\uDC00'); // Character.toCodePoint(c, d)
                          } else {
  //                            throw new JSONException("encodeUTF8 error", new MalformedInputException(1));
                              bytes[off++] = '?';
                              continue;
                          }
                      }
                  } else {
                      //
                      // Character.isLowSurrogate(c)
                      bytes[off++] = (byte) '?';
                      continue;
  //                        throw new JSONException("encodeUTF8 error", new MalformedInputException(1));
                  }
  
                  if (uc < 0) {
                      bytes[off++] = '?';
                  } else {
                      bytes[off] = (byte) (0xf0 | ((uc >> 18)));
                      bytes[off + 1] = (byte) (0x80 | ((uc >> 12) & 0x3f));
                      bytes[off + 2] = (byte) (0x80 | ((uc >> 6) & 0x3f));
                      bytes[off + 3] = (byte) (0x80 | (uc & 0x3f));
                      off += 4;
                      i++; // 2 chars
                  }
              } else if (ch > 0x07FF) {
                  bytes[off] = (byte) (0xE0 | ((ch >> 12) & 0x0F));
                  bytes[off + 1] = (byte) (0x80 | ((ch >> 6) & 0x3F));
                  bytes[off + 2] = (byte) (0x80 | (ch & 0x3F));
                  off += 3;
              } else {
                  bytes[off] = (byte) (0xC0 | ((ch >> 6) & 0x1F));
                  bytes[off + 1] = (byte) (0x80 | (ch & 0x3F));
                  off += 2;
              }
          }
  
          if (quoted) {
              bytes[off++] = (byte) quote;
          }
          this.off = off;
      }
  
      @Override
      public final void writeChar(char ch) {
          int off = this.off;
          byte[] bytes = this.bytes;
          if (off + 8 > bytes.length) {
              bytes = grow(off + 8);
          }
          bytes[off++] = (byte) quote;
          if (ch <= 0x007F) {
              switch (ch) {
                  case '\\':
                  case '\n':
                  case '\r':
                  case '\f':
                  case '\b':
                  case '\t':
                      StringUtils.writeEscapedChar(bytes, off, ch);
                      off += 2;
                      break;
                  case 0:
                  case 1:
                  case 2:
                  case 3:
                  case 4:
                  case 5:
                  case 6:
                  case 7:
                  case 11:
                  case 14:
                  case 15:
                  case 16:
                  case 17:
                  case 18:
                  case 19:
                  case 20:
                  case 21:
                  case 22:
                  case 23:
                  case 24:
                  case 25:
                  case 26:
                  case 27:
                  case 28:
                  case 29:
                  case 30:
                  case 31:
                      StringUtils.writeU4Hex2(bytes, off, ch);
                      off += 6;
                      break;
                  default:
                      if (ch == quote) {
                          bytes[off] = '\\';
                          bytes[off + 1] = (byte) quote;
                          off += 2;
                      } else {
                          bytes[off++] = (byte) ch;
                      }
                      break;
              }
          } else if (ch >= '\uD800' && ch < ('\uDFFF' + 1)) { //  //Character.isSurrogate(c)
              throw new JSONException("illegal char " + ch);
          } else if (ch > 0x07FF) {
              bytes[off] = (byte) (0xE0 | ((ch >> 12) & 0x0F));
              bytes[off + 1] = (byte) (0x80 | ((ch >> 6) & 0x3F));
              bytes[off + 2] = (byte) (0x80 | (ch & 0x3F));
              off += 3;
          } else {
              bytes[off] = (byte) (0xC0 | ((ch >> 6) & 0x1F));
              bytes[off + 1] = (byte) (0x80 | (ch & 0x3F));
              off += 2;
          }
  
          bytes[off] = (byte) quote;
          this.off = off + 1;
      }
  
      @Override
      public final void writeUUID(UUID value) {
          if (value == null) {
              writeNull();
              return;
          }
  
          int off = this.off;
          int minCapacity = off + 38;
          byte[] bytes = this.bytes;
          if (minCapacity > bytes.length) {
              bytes = grow(minCapacity);
          }
          byte quote = (byte) this.quote;
  
          final long base = ARRAY_BYTE_BASE_OFFSET + off;
          UNSAFE.putByte(bytes, base, quote);
          UNSAFE.putByte(bytes, base + 9, (byte) '-');
          UNSAFE.putByte(bytes, base + 14, (byte) '-');
          UNSAFE.putByte(bytes, base + 19, (byte) '-');
          UNSAFE.putByte(bytes, base + 24, (byte) '-');
          UNSAFE.putByte(bytes, base + 37, quote);
          long msb = value.getMostSignificantBits();
          long lsb = value.getLeastSignificantBits();
          long x = msb, x0 = hex8(x >>> 32), x1 = hex8(x);
          UNSAFE.putLong(bytes, base + 1, x0);
          UNSAFE.putInt(bytes, base + 10, (int) x1);
          UNSAFE.putInt(bytes, base + 15, (int) (x1 >>> 32));
          x = lsb;
          x0 = hex8(x >>> 32);
          x1 = hex8(x);
          UNSAFE.putInt(bytes, base + 20, (int) (x0));
          UNSAFE.putInt(bytes, base + 25, (int) (x0 >>> 32));
          UNSAFE.putLong(bytes, base + 29, x1);
          this.off += 38;
      }
  
      /**
       * Extract the least significant 4 bytes from the input integer i, convert each byte into its corresponding 2-digit
       * hexadecimal representation, concatenate these hexadecimal strings into one continuous string, and then interpret
       * this string as a hexadecimal number to form and return a long value.
       */
      private static long hex8(long i) {
          i = expand(i);
          /*
              Use long to simulate vector operations and generate 8 hexadecimal characters at a time.
              ------------
              0  = 0b0000_0000 => m = ((i + 6) & 0x10); (m << 1) + (m >> 1) - (m >> 4) => 0  + 0x30 + (i & 0xF) => '0'
              1  = 0b0000_0001 => m = ((i + 6) & 0x10); (m << 1) + (m >> 1) - (m >> 4) => 0  + 0x30 + (i & 0xF) => '1'
              2  = 0b0000_0010 => m = ((i + 6) & 0x10); (m << 1) + (m >> 1) - (m >> 4) => 0  + 0x30 + (i & 0xF) => '2'
              3  = 0b0000_0011 => m = ((i + 6) & 0x10); (m << 1) + (m >> 1) - (m >> 4) => 0  + 0x30 + (i & 0xF) => '3'
              4  = 0b0000_0100 => m = ((i + 6) & 0x10); (m << 1) + (m >> 1) - (m >> 4) => 0  + 0x30 + (i & 0xF) => '4'
              5  = 0b0000_0101 => m = ((i + 6) & 0x10); (m << 1) + (m >> 1) - (m >> 4) => 0  + 0x30 + (i & 0xF) => '5'
              6  = 0b0000_0110 => m = ((i + 6) & 0x10); (m << 1) + (m >> 1) - (m >> 4) => 0  + 0x30 + (i & 0xF) => '6'
              7  = 0b0000_0111 => m = ((i + 6) & 0x10); (m << 1) + (m >> 1) - (m >> 4) => 0  + 0x30 + (i & 0xF) => '7'
              8  = 0b0000_1000 => m = ((i + 6) & 0x10); (m << 1) + (m >> 1) - (m >> 4) => 0  + 0x30 + (i & 0xF) => '8'
              9  = 0b0000_1001 => m = ((i + 6) & 0x10); (m << 1) + (m >> 1) - (m >> 4) => 0  + 0x30 + (i & 0xF) => '9'
              10 = 0b0000_1010 => m = ((i + 6) & 0x10); (m << 1) + (m >> 1) - (m >> 4) => 39 + 0x30 + (i & 0xF) => 'a'
              11 = 0b0000_1011 => m = ((i + 6) & 0x10); (m << 1) + (m >> 1) - (m >> 4) => 39 + 0x30 + (i & 0xF) => 'b'
              12 = 0b0000_1100 => m = ((i + 6) & 0x10); (m << 1) + (m >> 1) - (m >> 4) => 39 + 0x30 + (i & 0xF) => 'c'
              13 = 0b0000_1101 => m = ((i + 6) & 0x10); (m << 1) + (m >> 1) - (m >> 4) => 39 + 0x30 + (i & 0xF) => 'd'
              14 = 0b0000_1110 => m = ((i + 6) & 0x10); (m << 1) + (m >> 1) - (m >> 4) => 39 + 0x30 + (i & 0xF) => 'e'
              15 = 0b0000_1111 => m = ((i + 6) & 0x10); (m << 1) + (m >> 1) - (m >> 4) => 39 + 0x30 + (i & 0xF) => 'f'
           */
          long m = (i + 0x0606_0606_0606_0606L) & 0x1010_1010_1010_1010L;
          long v = ((m << 1) + (m >> 1) - (m >> 4)) + 0x3030_3030_3030_3030L + i;
          if (!BIG_ENDIAN) {
              v = Long.reverseBytes(v);
          }
          return v;
      }
  
      /**
       * A faster alternative that is functionally equivalent to Long.expand(i, 0x0F0F_0F0F_0F0F_0F0FL)
       */
      private static long expand(long i) {
          long t = i << 16;
          i = (i & ~0xFFFF00000000L) | (t & 0xFFFF00000000L);
          t = i << 8;
          i = (i & ~0xFF000000FF0000L) | (t & 0xFF000000FF0000L);
          t = i << 4;
          i = (i & ~0xF000F000F000F00L) | (t & 0xF000F000F000F00L);
          return i & 0x0F0F_0F0F_0F0F_0F0FL;
      }
  
      @Override
      public final void writeRaw(String str) {
          char[] chars = getCharArray(str);
          int off = this.off;
          int minCapacity = off
                  + chars.length * 3; // utf8 3 bytes
  
          byte[] bytes = this.bytes;
          if (minCapacity > bytes.length) {
              bytes = grow(minCapacity);
          }
          for (int i = 0; i < chars.length; i++) {
              char c = chars[i];
              if ((c >= 0x0001) && (c <= 0x007F)) {
                  bytes[off++] = (byte) c;
              } else if (c > 0x07FF) {
                  bytes[off] = (byte) (0xE0 | ((c >> 12) & 0x0F));
                  bytes[off + 1] = (byte) (0x80 | ((c >> 6) & 0x3F));
                  bytes[off + 2] = (byte) (0x80 | (c & 0x3F));
                  off += 3;
              } else {
                  bytes[off] = (byte) (0xC0 | ((c >> 6) & 0x1F));
                  bytes[off + 1] = (byte) (0x80 | (c & 0x3F));
                  off += 2;
              }
          }
          this.off = off;
      }
  
      @Override
      public final void writeRaw(byte[] raw) {
          int off = this.off;
          int minCapacity = off + raw.length;
          if (minCapacity > bytes.length) {
              grow(minCapacity);
          }
          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 + pretty * level;
          byte[] bytes = this.bytes;
          if (minCapacity > bytes.length) {
              bytes = grow(minCapacity);
          }
          if (startObject) {
              startObject = false;
          } else {
              bytes[off++] = ',';
              if (pretty != PRETTY_NON) {
                  off = indent(bytes, off);
              }
          }
          System.arraycopy(name, 0, bytes, off, name.length);
          this.off = off + name.length;
      }
  
      @Override
      public final void writeName2Raw(long name) {
          int off = this.off;
          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 != PRETTY_NON) {
                  off = indent(bytes, off);
              }
          }
  
          UNSAFE.putLong(bytes, ARRAY_BYTE_BASE_OFFSET + off, name);
          this.off = off + 5;
      }
  
      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 + pretty * level;
          byte[] bytes = this.bytes;
          if (minCapacity > bytes.length) {
              bytes = grow(minCapacity);
          }
          if (startObject) {
              startObject = false;
          } else {
              bytes[off++] = ',';
              if (pretty != PRETTY_NON) {
                  off = indent(bytes, off);
              }
          }
  
          UNSAFE.putLong(bytes, ARRAY_BYTE_BASE_OFFSET + off, name);
          this.off = off + 6;
      }
  
      @Override
      public final void writeName4Raw(long name) {
          int off = this.off;
          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 != PRETTY_NON) {
                  off = indent(bytes, off);
              }
          }
  
          UNSAFE.putLong(bytes, ARRAY_BYTE_BASE_OFFSET + off, name);
          this.off = off + 7;
      }
  
      @Override
      public final void writeName5Raw(long name) {
          int off = this.off;
          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 != PRETTY_NON) {
                  off = indent(bytes, off);
              }
          }
  
          UNSAFE.putLong(bytes, ARRAY_BYTE_BASE_OFFSET + off, name);
          this.off = off + 8;
      }
  
      @Override
      public final void writeName6Raw(long name) {
          int off = this.off;
          int minCapacity = off + 11 + pretty * level;
          byte[] bytes = this.bytes;
          if (minCapacity > bytes.length) {
              bytes = grow(minCapacity);
          }
          if (startObject) {
              startObject = false;
          } else {
              bytes[off++] = ',';
              if (pretty != PRETTY_NON) {
                  off = indent(bytes, off);
              }
          }
  
          UNSAFE.putLong(bytes, ARRAY_BYTE_BASE_OFFSET + off, name);
          bytes[off + 8] = ':';
          this.off = off + 9;
      }
  
      @Override
      public final void writeName7Raw(long name) {
          int off = this.off;
          int minCapacity = off + 12 + pretty * level;
          byte[] bytes = this.bytes;
          if (minCapacity > bytes.length) {
              bytes = grow(minCapacity);
          }
          if (startObject) {
              startObject = false;
          } else {
              bytes[off++] = ',';
              if (pretty != PRETTY_NON) {
                  off = indent(bytes, off);
              }
          }
  
          UNSAFE.putLong(bytes, ARRAY_BYTE_BASE_OFFSET + off, name);
          bytes[off + 8] = (byte) quote;
          bytes[off + 9] = ':';
          this.off = off + 10;
      }
  
      @Override
      public void writeName8Raw(long name) {
          int off = this.off;
          int minCapacity = off
                  + 13 // 8 + quote 2 + comma 1 + colon 1 + pretty 1
                  + pretty * level;
          byte[] bytes = this.bytes;
          if (minCapacity > bytes.length) {
              bytes = grow(minCapacity);
          }
          if (startObject) {
              startObject = false;
          } else {
              bytes[off++] = ',';
              if (pretty != PRETTY_NON) {
                  off = indent(bytes, off);
              }
          }
  
          bytes[off] = (byte) quote;
          UNSAFE.putLong(bytes, ARRAY_BYTE_BASE_OFFSET + off + 1, name);
          UNSAFE.putShort(bytes, ARRAY_BYTE_BASE_OFFSET + off + 9, useSingleQuote ? QUOTE_COLON : QUOTE2_COLON);
          this.off = off + 11;
      }
  
      @Override
      public final void writeName9Raw(long name0, int name1) {
          int off = this.off;
          int minCapacity = off + 14 + pretty * level;
          byte[] bytes = this.bytes;
          if (minCapacity > bytes.length) {
              bytes = grow(minCapacity);
          }
          if (startObject) {
              startObject = false;
          } else {
              bytes[off++] = ',';
              if (pretty != PRETTY_NON) {
                  off = indent(bytes, off);
              }
          }
  
          UNSAFE.putLong(bytes, ARRAY_BYTE_BASE_OFFSET + off, name0);
          UNSAFE.putInt(bytes, ARRAY_BYTE_BASE_OFFSET + off + 8, name1);
          this.off = off + 12;
      }
  
      @Override
      public final void writeName10Raw(long name0, long name1) {
          int off = this.off;
          int minCapacity = off + 18 + pretty * level;
          byte[] bytes = this.bytes;
          if (minCapacity > bytes.length) {
              bytes = grow(minCapacity);
          }
          if (startObject) {
              startObject = false;
          } else {
              bytes[off++] = ',';
              if (pretty != PRETTY_NON) {
                  off = indent(bytes, off);
              }
          }
  
          UNSAFE.putLong(bytes, ARRAY_BYTE_BASE_OFFSET + off, name0);
          UNSAFE.putLong(bytes, ARRAY_BYTE_BASE_OFFSET + off + 8, name1);
          this.off = off + 13;
      }
  
      @Override
      public final void writeName11Raw(long name0, long name1) {
          int off = this.off;
          int minCapacity = off + 18 + pretty * level;
          byte[] bytes = this.bytes;
          if (minCapacity > bytes.length) {
              bytes = grow(minCapacity);
          }
          if (startObject) {
              startObject = false;
          } else {
              bytes[off++] = ',';
              if (pretty != PRETTY_NON) {
                  off = indent(bytes, off);
              }
          }
  
          UNSAFE.putLong(bytes, ARRAY_BYTE_BASE_OFFSET + off, name0);
          UNSAFE.putLong(bytes, ARRAY_BYTE_BASE_OFFSET + off + 8, name1);
          this.off = off + 14;
      }
  
      @Override
      public final void writeName12Raw(long name0, long name1) {
          int off = this.off;
          int minCapacity = off + 18 + pretty * level;
          byte[] bytes = this.bytes;
          if (minCapacity > bytes.length) {
              bytes = grow(minCapacity);
          }
          if (startObject) {
              startObject = false;
          } else {
              bytes[off++] = ',';
              if (pretty != PRETTY_NON) {
                  off = indent(bytes, off);
              }
          }
  
          UNSAFE.putLong(bytes, ARRAY_BYTE_BASE_OFFSET + off, name0);
          UNSAFE.putLong(bytes, ARRAY_BYTE_BASE_OFFSET + off + 8, name1);
          this.off = off + 15;
      }
  
      @Override
      public final void writeName13Raw(long name0, long name1) {
          int off = this.off;
          int minCapacity = off + 18 + pretty * level;
          byte[] bytes = this.bytes;
          if (minCapacity > bytes.length) {
              bytes = grow(minCapacity);
          }
          if (startObject) {
              startObject = false;
          } else {
              bytes[off++] = ',';
              if (pretty != PRETTY_NON) {
                  off = indent(bytes, off);
              }
          }
  
          UNSAFE.putLong(bytes, ARRAY_BYTE_BASE_OFFSET + off, name0);
          UNSAFE.putLong(bytes, ARRAY_BYTE_BASE_OFFSET + off + 8, name1);
          this.off = off + 16;
      }
  
      @Override
      public final void writeName14Raw(long name0, long name1) {
          int off = this.off;
          int minCapacity = off + 19 + pretty * level;
          byte[] bytes = this.bytes;
          if (minCapacity > bytes.length) {
              bytes = grow(minCapacity);
          }
          if (startObject) {
              startObject = false;
          } else {
              bytes[off++] = ',';
              if (pretty != PRETTY_NON) {
                  off = indent(bytes, off);
              }
          }
  
          UNSAFE.putLong(bytes, ARRAY_BYTE_BASE_OFFSET + off, name0);
          UNSAFE.putLong(bytes, ARRAY_BYTE_BASE_OFFSET + off + 8, name1);
          bytes[off + 16] = ':';
          this.off = off + 17;
      }
  
      @Override
      public final void writeName15Raw(long name0, long name1) {
          int off = this.off;
          int minCapacity = off + 20 + pretty * level;
          byte[] bytes = this.bytes;
          if (minCapacity > bytes.length) {
              bytes = grow(minCapacity);
          }
          if (startObject) {
              startObject = false;
          } else {
              bytes[off++] = ',';
              if (pretty != PRETTY_NON) {
                  off = indent(bytes, off);
              }
          }
  
          UNSAFE.putLong(bytes, ARRAY_BYTE_BASE_OFFSET + off, name0);
          UNSAFE.putLong(bytes, ARRAY_BYTE_BASE_OFFSET + off + 8, name1);
          UNSAFE.putShort(bytes, ARRAY_BYTE_BASE_OFFSET + off + 16, useSingleQuote ? QUOTE_COLON : QUOTE2_COLON);
          this.off = off + 18;
      }
  
      @Override
      public final void writeName16Raw(long name0, long name1) {
          int off = this.off;
          int minCapacity = off + 21 + pretty * level;
          byte[] bytes = this.bytes;
          if (minCapacity > bytes.length) {
              bytes = grow(minCapacity);
          }
          if (startObject) {
              startObject = false;
          } else {
              bytes[off++] = ',';
              if (pretty != PRETTY_NON) {
                  off = indent(bytes, off);
              }
          }
  
          bytes[off++] = (byte) quote;
          UNSAFE.putLong(bytes, ARRAY_BYTE_BASE_OFFSET + off, name0);
          UNSAFE.putLong(bytes, ARRAY_BYTE_BASE_OFFSET + off + 8, name1);
          UNSAFE.putShort(bytes, ARRAY_BYTE_BASE_OFFSET + off + 16, useSingleQuote ? QUOTE_COLON : QUOTE2_COLON);
          this.off = off + 18;
      }
  
      public final void writeRaw(byte b) {
          int off = this.off;
          grow1(off)[off] = b;
          this.off = off + 1;
      }
  
      @Override
      public final void writeRaw(char ch) {
          if (ch > 128) {
              throw new JSONException("not support " + ch);
          }
  
          if (off == bytes.length) {
              grow0(off + 1);
          }
          bytes[off++] = (byte) ch;
      }
  
      @Override
      public final void writeRaw(char c0, char c1) {
          if (c0 > 128 || c1 > 128) {
              throw new JSONException("not support " + c0 + ", " + c1);
          }
  
          int off = this.off;
          byte[] bytes = this.bytes;
          if (off + 2 > bytes.length) {
              bytes = grow(off + 2);
          }
          bytes[off] = (byte) c0;
          bytes[off + 1] = (byte) c1;
          this.off = off + 2;
      }
  
      @Override
      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) {
              bytes[off++] = ',';
              if (pretty != PRETTY_NON) {
                  off = indent(bytes, off);
              }
          }
          startObject = false;
          System.arraycopy(name, coff, bytes, off, len);
          this.off = off + len;
      }
  
      public final Object ensureCapacity(int minCapacity) {
          byte[] bytes = this.bytes;
          if (minCapacity >= bytes.length) {
              this.bytes = bytes = Arrays.copyOf(bytes, newCapacity(minCapacity, bytes.length));
          }
          return bytes;
      }
  
      private byte[] grow(int minCapacity) {
          grow0(minCapacity);
          return bytes;
      }
  
      private byte[] grow1(int off) {
          byte[] bytes = this.bytes;
          if (off == bytes.length) {
              bytes = grow(off + 1);
          }
          return bytes;
      }
  
      private void grow0(int minCapacity) {
          // minCapacity is usually close to size, so this is a win:
          bytes = Arrays.copyOf(bytes, newCapacity(minCapacity, bytes.length));
      }
  
      public final void writeInt32(int[] values) {
          if (values == null) {
              writeNull();
              return;
          }
  
          boolean writeAsString = (context.features & MASK_WRITE_NON_STRING_VALUE_AS_STRING) != 0;
  
          int off = this.off;
          int minCapacity = off + values.length * 13 + 2;
          byte[] bytes = this.bytes;
          if (minCapacity > bytes.length) {
              bytes = grow(minCapacity);
          }
          bytes[off++] = '[';
  
          for (int i = 0; i < values.length; i++) {
              if (i != 0) {
                  bytes[off++] = ',';
              }
              if (writeAsString) {
                  bytes[off++] = (byte) quote;
              }
              off = IOUtils.writeInt32(bytes, off, values[i]);
              if (writeAsString) {
                  bytes[off++] = (byte) quote;
              }
          }
  
          bytes[off] = ']';
          this.off = off + 1;
      }
  
      @Override
      public final void writeInt8(byte i) {
          boolean writeAsString = (context.features & MASK_WRITE_NON_STRING_VALUE_AS_STRING) != 0;
  
          int off = this.off;
          int minCapacity = off + 5;
          byte[] bytes = this.bytes;
          if (minCapacity > bytes.length) {
              bytes = grow(minCapacity);
          }
          if (writeAsString) {
              bytes[off++] = (byte) quote;
          }
          off = IOUtils.writeInt8(bytes, off, i);
          if (writeAsString) {
              bytes[off++] = (byte) quote;
          }
          this.off = off;
      }
  
      @Override
      public final void writeInt8(byte[] values) {
          if (values == null) {
              writeNull();
              return;
          }
  
          boolean writeAsString = (context.features & MASK_WRITE_NON_STRING_VALUE_AS_STRING) != 0;
  
          int off = this.off;
          int minCapacity = off + values.length * 5 + 2;
          byte[] bytes = this.bytes;
          if (minCapacity > bytes.length) {
              bytes = grow(minCapacity);
          }
          bytes[off++] = '[';
  
          for (int i = 0; i < values.length; i++) {
              if (i != 0) {
                  bytes[off++] = ',';
              }
              if (writeAsString) {
                  bytes[off++] = (byte) quote;
              }
              off = IOUtils.writeInt8(bytes, off, values[i]);
              if (writeAsString) {
                  bytes[off++] = (byte) quote;
              }
          }
  
          bytes[off] = ']';
          this.off = off + 1;
      }
  
      @Override
      public final void writeInt16(short i) {
          boolean writeAsString = (context.features & WriteNonStringValueAsString.mask) != 0;
  
          int off = this.off;
          int minCapacity = off + 7;
          byte[] bytes = this.bytes;
          if (minCapacity > bytes.length) {
              bytes = grow(minCapacity);
          }
          if (writeAsString) {
              bytes[off++] = (byte) quote;
          }
          off = IOUtils.writeInt16(bytes, off, i);
          if (writeAsString) {
              bytes[off++] = (byte) quote;
          }
          this.off = off;
      }
  
      @Override
      public final void writeInt32(Integer i) {
          if (i == null) {
              writeNumberNull();
          } else {
              writeInt32(i.intValue());
          }
      }
  
      @Override
      public final void writeInt32(int i) {
          boolean writeAsString = (context.features & MASK_WRITE_NON_STRING_VALUE_AS_STRING) != 0;
  
          int off = this.off;
          int minCapacity = off + 13;
          byte[] bytes = this.bytes;
          if (minCapacity > bytes.length) {
              bytes = grow(minCapacity);
          }
          if (writeAsString) {
              bytes[off++] = (byte) quote;
          }
          off = IOUtils.writeInt32(bytes, off, i);
          if (writeAsString) {
              bytes[off++] = (byte) quote;
          }
          this.off = off;
      }
  
      @Override
      public final void writeListInt32(List<Integer> values) {
          if (values == null) {
              writeNull();
              return;
          }
  
          int size = values.size();
          boolean writeAsString = (context.features & MASK_WRITE_NON_STRING_VALUE_AS_STRING) != 0;
          int off = this.off;
          int minCapacity = off + 2 + size * 23;
          byte[] bytes = this.bytes;
          if (minCapacity > bytes.length) {
              bytes = grow(minCapacity);
          }
          bytes[off++] = '[';
  
          for (int i = 0; i < size; i++) {
              if (i != 0) {
                  bytes[off++] = ',';
              }
              Number item = values.get(i);
              if (item == null) {
                  IOUtils.putNULL(bytes, off);
                  off += 4;
                  continue;
              }
  
              int v = item.intValue();
              if (writeAsString) {
                  bytes[off++] = (byte) quote;
              }
              off = IOUtils.writeInt32(bytes, off, v);
              if (writeAsString) {
                  bytes[off++] = (byte) quote;
              }
          }
  
          bytes[off] = ']';
          this.off = off + 1;
      }
  
      @Override
      public final void writeInt64(long[] values) {
          if (values == null) {
              writeNull();
              return;
          }
  
          int off = this.off;
          int minCapacity = off + 2 + values.length * 23;
          byte[] bytes = this.bytes;
          if (minCapacity > bytes.length) {
              bytes = grow(minCapacity);
          }
          bytes[off++] = '[';
  
          for (int i = 0; i < values.length; i++) {
              if (i != 0) {
                  bytes[off++] = ',';
              }
              long v = values[i];
              boolean writeAsString = isWriteAsString(v, context.features);
              if (writeAsString) {
                  bytes[off++] = (byte) quote;
              }
              off = IOUtils.writeInt64(bytes, off, v);
              if (writeAsString) {
                  bytes[off++] = (byte) quote;
              }
          }
  
          bytes[off] = ']';
          this.off = off + 1;
      }
  
      @Override
      public final void writeListInt64(List<Long> values) {
          if (values == null) {
              writeNull();
              return;
          }
  
          int size = values.size();
          int off = this.off;
          int minCapacity = off + 2 + size * 23;
          byte[] bytes = this.bytes;
          if (minCapacity > bytes.length) {
              bytes = grow(minCapacity);
          }
          bytes[off++] = '[';
  
          for (int i = 0; i < size; i++) {
              if (i != 0) {
                  bytes[off++] = ',';
              }
              Long item = values.get(i);
              if (item == null) {
                  IOUtils.putNULL(bytes, off);
                  off += 4;
                  continue;
              }
  
              long v = item;
              boolean writeAsString = isWriteAsString(v, context.features);
              if (writeAsString) {
                  bytes[off++] = (byte) quote;
              }
              off = IOUtils.writeInt64(bytes, off, v);
              if (writeAsString) {
                  bytes[off++] = (byte) quote;
              }
          }
  
          bytes[off] = ']';
          this.off = off + 1;
      }
  
      @Override
      public final void writeInt64(long i) {
          final long features = context.features;
          int off = this.off;
          int minCapacity = off + 23;
          byte[] bytes = this.bytes;
          if (minCapacity > bytes.length) {
              bytes = grow(minCapacity);
          }
          boolean writeAsString = isWriteAsString(i, features);
          if (writeAsString) {
              bytes[off++] = (byte) quote;
          }
          off = IOUtils.writeInt64(bytes, off, i);
          if (writeAsString) {
              bytes[off++] = (byte) quote;
          } else if ((features & MASK_WRITE_CLASS_NAME) != 0
                  && (features & MASK_NOT_WRITE_NUMBER_CLASS_NAME) == 0
                  && i >= Integer.MIN_VALUE && i <= Integer.MAX_VALUE
          ) {
              bytes[off++] = 'L';
          }
          this.off = off;
      }
  
      @Override
      public final void writeInt64(Long i) {
          if (i == null) {
              writeNumberNull();
          } else {
              writeInt64(i.longValue());
          }
      }
  
      @Override
      public final void writeFloat(float value) {
          boolean writeAsString = (context.features & MASK_WRITE_NON_STRING_VALUE_AS_STRING) != 0;
  
          int off = this.off;
          int minCapacity = off + 17;
          byte[] bytes = this.bytes;
          if (minCapacity > bytes.length) {
              bytes = grow(minCapacity);
          }
  
          if (writeAsString) {
              bytes[off++] = '"';
          }
  
          off = NumberUtils.writeFloat(bytes, off, value, true);
  
          if (writeAsString) {
              bytes[off++] = '"';
          }
          this.off = off;
      }
  
      @Override
      public final void writeDouble(double value) {
          boolean writeAsString = (context.features & MASK_WRITE_NON_STRING_VALUE_AS_STRING) != 0;
  
          int off = this.off;
          int minCapacity = off + 26;
          byte[] bytes = this.bytes;
          if (minCapacity > bytes.length) {
              bytes = grow(minCapacity);
          }
          if (writeAsString) {
              bytes[off++] = '"';
          }
  
          off = NumberUtils.writeDouble(bytes, off, value, true);
  
          if (writeAsString) {
              bytes[off++] = '"';
          }
          this.off = off;
      }
  
      @Override
      public final void writeFloat(float[] values) {
          if (values == null) {
              writeArrayNull();
              return;
          }
  
          boolean writeAsString = (context.features & MASK_WRITE_NON_STRING_VALUE_AS_STRING) != 0;
  
          int off = this.off;
          int minCapacity = off + values.length * (writeAsString ? 16 : 18) + 1;
          byte[] bytes = this.bytes;
          if (minCapacity > bytes.length) {
              bytes = grow(minCapacity);
          }
  
          bytes[off++] = '[';
          for (int i = 0; i < values.length; i++) {
              if (i != 0) {
                  bytes[off++] = ',';
              }
  
              if (writeAsString) {
                  bytes[off++] = '"';
              }
  
              off = NumberUtils.writeFloat(bytes, off, values[i], true);
  
              if (writeAsString) {
                  bytes[off++] = '"';
              }
          }
          bytes[off] = ']';
          this.off = off + 1;
      }
  
      @Override
      public final void writeDouble(double[] values) {
          if (values == null) {
              writeNull();
              return;
          }
  
          boolean writeAsString = (context.features & WriteNonStringValueAsString.mask) != 0;
  
          int off = this.off;
          int minCapacity = off + values.length * 27 + 1;
          byte[] bytes = this.bytes;
          if (minCapacity > bytes.length) {
              bytes = grow(minCapacity);
          }
          bytes[off++] = '[';
          for (int i = 0; i < values.length; i++) {
              if (i != 0) {
                  bytes[off++] = ',';
              }
  
              if (writeAsString) {
                  bytes[off++] = '"';
              }
  
              off = NumberUtils.writeDouble(bytes, off, values[i], true);
  
              if (writeAsString) {
                  bytes[off++] = '"';
              }
          }
          bytes[off] = ']';
          this.off = off + 1;
      }
  
      @Override
      public final void writeDateTime14(
              int year,
              int month,
              int dayOfMonth,
              int hour,
              int minute,
              int second
      ) {
          int off = this.off;
          int minCapacity = off + 16;
          byte[] bytes = this.bytes;
          if (minCapacity > bytes.length) {
              bytes = grow(minCapacity);
          }
          bytes[off] = (byte) quote;
          if (year < 0 || year > 9999) {
              throw illegalYear(year);
          }
          int y01 = year / 100;
          int y23 = year - y01 * 100;
          writeDigitPair(bytes, off + 1, y01);
          writeDigitPair(bytes, off + 3, y23);
          writeDigitPair(bytes, off + 5, month);
          writeDigitPair(bytes, off + 7, dayOfMonth);
          writeDigitPair(bytes, off + 9, hour);
          writeDigitPair(bytes, off + 11, minute);
          writeDigitPair(bytes, off + 13, second);
          bytes[off + 15] = (byte) quote;
          this.off = off + 16;
      }
  
      @Override
      public final void writeDateTime19(
              int year,
              int month,
              int dayOfMonth,
              int hour,
              int minute,
              int second) {
          int off = this.off;
          int minCapacity = off + 21;
          byte[] bytes = this.bytes;
          if (minCapacity > bytes.length) {
              bytes = grow(minCapacity);
          }
          bytes[off] = (byte) quote;
          off = IOUtils.writeLocalDate(bytes, off + 1, year, month, dayOfMonth);
          bytes[off] = ' ';
          IOUtils.writeLocalTime(bytes, off + 1, hour, minute, second);
          bytes[off + 9] = (byte) quote;
          this.off = off + 10;
      }
  
      @Override
      public final void writeLocalDate(LocalDate date) {
          if (date == null) {
              writeNull();
              return;
          }
  
          if (context.dateFormat != null
                  && writeLocalDateWithFormat(date)) {
              return;
          }
  
          int off = this.off;
          int minCapacity = off + 18;
          byte[] bytes = this.bytes;
          if (minCapacity > bytes.length) {
              bytes = grow(minCapacity);
          }
          bytes[off++] = (byte) quote;
          off = IOUtils.writeLocalDate(bytes, off, date.getYear(), date.getMonthValue(), date.getDayOfMonth());
          bytes[off] = (byte) quote;
          this.off = off + 1;
      }
  
      @Override
      public final void writeLocalDateTime(LocalDateTime dateTime) {
          int off = this.off;
          int minCapacity = off + 38;
          byte[] bytes = this.bytes;
          if (minCapacity > bytes.length) {
              bytes = grow(minCapacity);
          }
          bytes[off++] = (byte) quote;
          LocalDate localDate = dateTime.toLocalDate();
          off = IOUtils.writeLocalDate(bytes, off, localDate.getYear(), localDate.getMonthValue(), localDate.getDayOfMonth());
          bytes[off++] = ' ';
          off = IOUtils.writeLocalTime(bytes, off, dateTime.toLocalTime());
          bytes[off] = (byte) quote;
          this.off = off + 1;
      }
  
      @Override
      public final void writeDateYYYMMDD8(int year, int month, int dayOfMonth) {
          int off = this.off;
          int minCapacity = off + 10;
          byte[] bytes = this.bytes;
          if (minCapacity > bytes.length) {
              bytes = grow(minCapacity);
          }
          bytes[off] = (byte) quote;
          if (year < 0 || year > 9999) {
              throw illegalYear(year);
          }
          int y01 = year / 100;
          int y23 = year - y01 * 100;
          writeDigitPair(bytes, off + 1, y01);
          writeDigitPair(bytes, off + 3, y23);
          writeDigitPair(bytes, off + 5, month);
          writeDigitPair(bytes, off + 7, dayOfMonth);
          bytes[off + 9] = (byte) quote;
          this.off = off + 10;
      }
  
      @Override
      public final void writeDateYYYMMDD10(int year, int month, int dayOfMonth) {
          int off = this.off;
          int minCapacity = off + 13;
          byte[] bytes = this.bytes;
          if (minCapacity > bytes.length) {
              bytes = grow(minCapacity);
          }
          bytes[off++] = (byte) quote;
          off = IOUtils.writeLocalDate(bytes, off, year, month, dayOfMonth);
          bytes[off] = (byte) quote;
          this.off = off + 1;
      }
  
      @Override
      public final void writeTimeHHMMSS8(int hour, int minute, int second) {
          int off = this.off;
          int minCapacity = off + 10;
          byte[] bytes = this.bytes;
          if (minCapacity > bytes.length) {
              bytes = grow(minCapacity);
          }
          bytes[off] = (byte) quote;
          IOUtils.writeLocalTime(bytes, off + 1, hour, minute, second);
          bytes[off + 9] = (byte) quote;
          this.off = off + 10;
      }
  
      @Override
      public final void writeLocalTime(LocalTime time) {
          int off = this.off;
          int minCapacity = off + 20;
          byte[] bytes = this.bytes;
          if (minCapacity > bytes.length) {
              bytes = grow(minCapacity);
          }
          bytes[off++] = (byte) quote;
          off = IOUtils.writeLocalTime(bytes, off, time);
          bytes[off] = (byte) quote;
          this.off = off + 1;
      }
  
      @Override
      public final void writeZonedDateTime(ZonedDateTime dateTime) {
          if (dateTime == null) {
              writeNull();
              return;
          }
  
          ZoneId zone = dateTime.getZone();
          String zoneId = zone.getId();
          int zoneIdLength = zoneId.length();
          char firstZoneChar = '\0';
          int zoneSize;
          if (ZoneOffset.UTC == zone || (zoneIdLength <= 3 && ("UTC".equals(zoneId) || "Z".equals(zoneId)))) {
              zoneId = "Z";
              zoneSize = 1;
          } else if (zoneIdLength != 0 && ((firstZoneChar = zoneId.charAt(0)) == '+' || firstZoneChar == '-')) {
              zoneSize = zoneIdLength;
          } else {
              zoneSize = 2 + zoneIdLength;
          }
  
          int off = this.off;
          int minCapacity = off + zoneSize + 38;
          byte[] bytes = this.bytes;
          if (minCapacity > bytes.length) {
              bytes = grow(minCapacity);
          }
          bytes[off] = (byte) quote;
          LocalDate localDate = dateTime.toLocalDate();
          off = IOUtils.writeLocalDate(bytes, off + 1, localDate.getYear(), localDate.getMonthValue(), localDate.getDayOfMonth());
          bytes[off] = 'T';
          off = IOUtils.writeLocalTime(bytes, off + 1, dateTime.toLocalTime());
          if (zoneSize == 1) {
              bytes[off++] = 'Z';
          } else if (firstZoneChar == '+' || firstZoneChar == '-') {
              zoneId.getBytes(0, zoneIdLength, bytes, off);
              off += zoneIdLength;
          } else {
              bytes[off++] = '[';
              zoneId.getBytes(0, zoneIdLength, bytes, off);
              off += zoneIdLength;
              bytes[off++] = ']';
          }
          bytes[off] = (byte) quote;
          this.off = off + 1;
      }
  
      @Override
      public final void writeOffsetDateTime(OffsetDateTime dateTime) {
          if (dateTime == null) {
              writeNull();
              return;
          }
  
          int off = this.off;
          int minCapacity = off + 45;
          byte[] bytes = this.bytes;
          if (minCapacity > bytes.length) {
              bytes = grow(minCapacity);
          }
          bytes[off] = (byte) quote;
          LocalDateTime ldt = dateTime.toLocalDateTime();
          LocalDate date = ldt.toLocalDate();
          off = IOUtils.writeLocalDate(bytes, off + 1, date.getYear(), date.getMonthValue(), date.getDayOfMonth());
          bytes[off] = 'T';
          off = IOUtils.writeLocalTime(bytes, off + 1, ldt.toLocalTime());
  
          ZoneOffset offset = dateTime.getOffset();
          if (offset.getTotalSeconds() == 0) {
              bytes[off++] = 'Z';
          } else {
              String zoneId = offset.getId();
              zoneId.getBytes(0, zoneId.length(), bytes, off);
              off += zoneId.length();
          }
          bytes[off] = (byte) quote;
          this.off = off + 1;
      }
  
      public final void writeOffsetTime(OffsetTime time) {
          if (time == null) {
              writeNull();
              return;
          }
  
          int off = this.off;
          int minCapacity = off + 28;
          byte[] bytes = this.bytes;
          if (minCapacity > bytes.length) {
              bytes = grow(minCapacity);
          }
          bytes[off] = (byte) quote;
          off = IOUtils.writeLocalTime(bytes, off + 1, time.toLocalTime());
  
          ZoneOffset offset = time.getOffset();
          if (offset.getTotalSeconds() == 0) {
              bytes[off++] = 'Z';
          } else {
              String zoneId = offset.getId();
              zoneId.getBytes(0, zoneId.length(), bytes, off);
              off += zoneId.length();
          }
          bytes[off] = (byte) quote;
          this.off = off + 1;
      }
  
      @Override
      public final void writeBigInt(BigInteger value, long features) {
          if (value == null) {
              writeNumberNull();
              return;
          }
  
          if (isInt64(value) && features == 0) {
              writeInt64(value.longValue());
              return;
          }
  
          String str = value.toString(10);
  
          boolean writeAsString = isWriteAsString(value, context.features | features);
  
          int off = this.off;
          int strlen = str.length();
  
          int minCapacity = off + strlen + (writeAsString ? 2 : 0);
          byte[] bytes = this.bytes;
          if (minCapacity > bytes.length) {
              bytes = grow(minCapacity);
          }
          if (writeAsString) {
              bytes[off++] = '"';
          }
          str.getBytes(0, strlen, bytes, off);
          off += strlen;
          if (writeAsString) {
              bytes[off++] = '"';
          }
          this.off = off;
      }
  
      @Override
      public final void writeDateTimeISO8601(
              int year,
              int month,
              int dayOfMonth,
              int hour,
              int minute,
              int second,
              int millis,
              int offsetSeconds,
              boolean timeZone
      ) {
          int zonelen;
          if (timeZone) {
              zonelen = offsetSeconds == 0 ? 1 : 6;
          } else {
              zonelen = 0;
          }
  
          int off = this.off;
          int minCapacity = off + 25 + zonelen;
          byte[] bytes = this.bytes;
          if (minCapacity > bytes.length) {
              bytes = grow(minCapacity);
          }
          bytes[off] = (byte) quote;
          off = IOUtils.writeLocalDate(bytes, off + 1, year, month, dayOfMonth);
          bytes[off] = (byte) (timeZone ? 'T' : ' ');
          IOUtils.writeLocalTime(bytes, off + 1, hour, minute, second);
          off += 9;
  
          if (millis > 0) {
              int div = millis / 10;
              int div2 = div / 10;
              final int rem1 = millis - div * 10;
  
              if (rem1 != 0) {
                  putIntLE(bytes, off, DIGITS_K_32[millis & 0x3ff] & 0xffffff00 | '.');
                  off += 4;
              } else {
                  bytes[off++] = '.';
                  final int rem2 = div - div2 * 10;
                  if (rem2 != 0) {
                      writeDigitPair(bytes, off, div);
                      off += 2;
                  } else {
                      bytes[off++] = (byte) (div2 + '0');
                  }
              }
          }
  
          if (timeZone) {
              int offset = offsetSeconds / 3600;
              if (offsetSeconds == 0) {
                  bytes[off++] = 'Z';
              } else {
                  int offsetAbs = Math.abs(offset);
                  bytes[off] = offset >= 0 ? (byte) '+' : (byte) '-';
                  writeDigitPair(bytes, off + 1, offsetAbs);
                  bytes[off + 3] = ':';
                  int offsetMinutes = (offsetSeconds - offset * 3600) / 60;
                  if (offsetMinutes < 0) {
                      offsetMinutes = -offsetMinutes;
                  }
                  writeDigitPair(bytes, off + 4, offsetMinutes);
                  off += 6;
              }
          }
          bytes[off] = (byte) quote;
          this.off = off + 1;
      }
  
      @Override
      public final void writeDecimal(BigDecimal value, long features, DecimalFormat format) {
          if (value == null) {
              writeDecimalNull();
              return;
          }
  
          if (format != null) {
              String str = format.format(value);
              writeRaw(str);
              return;
          }
  
          features |= context.features;
  
          int precision = value.precision();
          boolean writeAsString = isWriteAsString(value, features);
  
          int off = this.off;
          int minCapacity = off + precision + Math.abs(value.scale()) + 7;
          byte[] bytes = this.bytes;
          if (minCapacity > bytes.length) {
              bytes = grow(minCapacity);
          }
          if (writeAsString) {
              bytes[off++] = '"';
          }
  
          boolean asPlain = (features & WriteBigDecimalAsPlain.mask) != 0;
          long unscaleValue;
          int scale;
          if (precision < 19
                  && (scale = value.scale()) >= 0
                  && FIELD_DECIMAL_INT_COMPACT_OFFSET != -1
                  && (unscaleValue = UNSAFE.getLong(value, FIELD_DECIMAL_INT_COMPACT_OFFSET)) != Long.MIN_VALUE
                  && !asPlain
          ) {
              off = IOUtils.writeDecimal(bytes, off, unscaleValue, scale);
          } else {
              String str = asPlain ? value.toPlainString() : value.toString();
              str.getBytes(0, str.length(), bytes, off);
              off += str.length();
          }
  
          if (writeAsString) {
              bytes[off++] = '"';
          }
          this.off = off;
      }
  
      @Override
      public final void writeNameRaw(char[] chars) {
          throw new JSONException("UnsupportedOperation");
      }
  
      @Override
      public final void writeNameRaw(char[] bytes, int offset, int len) {
          throw new JSONException("UnsupportedOperation");
      }
  
      @Override
      public final void write(Map<?, ?> map) {
          if (pretty != PRETTY_NON) {
              super.write(map);
              return;
          }
  
          if (map == null) {
              this.writeNull();
              return;
          }
  
          long features = context.features;
          if ((features & NONE_DIRECT_FEATURES) != 0) {
              ObjectWriter objectWriter = context.getObjectWriter(map.getClass());
              objectWriter.write(this, map, null, null, 0);
              return;
          }
  
          if (off == bytes.length) {
              grow(off + 1);
          }
          bytes[off++] = '{';
  
          boolean first = true;
          for (Map.Entry entry : map.entrySet()) {
              Object value = entry.getValue();
              if (value == null && (features & MASK_WRITE_MAP_NULL_VALUE) == 0) {
                  continue;
              }
  
              if (!first) {
                  if (off == bytes.length) {
                      grow0(off + 1);
                  }
                  bytes[off++] = ',';
              }
  
              first = false;
              Object key = entry.getKey();
              if (key instanceof String) {
                  writeString((String) key);
              } else {
                  writeAny(key);
              }
  
              if (off == bytes.length) {
                  grow0(off + 1);
              }
              bytes[off++] = ':';
  
              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);
          }
  
          if (off == bytes.length) {
              grow(off + 1);
          }
          bytes[off++] = '}';
      }
  
      @Override
      public final void write(List array) {
          if (array == null) {
              this.writeArrayNull();
              return;
          }
  
          if ((context.features & (MASK_REFERENCE_DETECTION | MASK_PRETTY_FORMAT | MASK_NOT_WRITE_EMPTY_ARRAY | MASK_NOT_WRITE_DEFAULT_VALUE)) != 0) {
              ObjectWriter objectWriter = context.getObjectWriter(array.getClass());
              objectWriter.write(this, array, null, null, 0);
              return;
          }
  
          if (off == bytes.length) {
              grow(off + 1);
          }
          bytes[off++] = '[';
  
          boolean first = true;
          for (int i = 0; i < array.size(); i++) {
              Object o = array.get(i);
              if (!first) {
                  if (off == bytes.length) {
                      grow(off + 1);
                  }
                  bytes[off++] = ',';
              }
              first = false;
  
              if (o == null) {
                  writeNull();
                  continue;
              }
  
              Class<?> valueClass = o.getClass();
              if (valueClass == String.class) {
                  writeString((String) o);
                  continue;
              }
  
              if (valueClass == Integer.class) {
                  writeInt32((Integer) o);
                  continue;
              }
  
              if (valueClass == Long.class) {
                  writeInt64((Long) o);
                  continue;
              }
  
              if (valueClass == Boolean.class) {
                  writeBool((Boolean) o);
                  continue;
              }
  
              if (valueClass == BigDecimal.class) {
                  writeDecimal((BigDecimal) o, 0, null);
                  continue;
              }
  
              if (valueClass == JSONArray.class) {
                  write((JSONArray) o);
                  continue;
              }
  
              if (valueClass == JSONObject.class) {
                  write((JSONObject) o);
                  continue;
              }
  
              ObjectWriter objectWriter = context.getObjectWriter(valueClass, valueClass);
              objectWriter.write(this, o, null, null, 0);
          }
          if (off == bytes.length) {
              grow(off + 1);
          }
          bytes[off++] = ']';
      }
  
      public void writeBool(boolean value) {
          int minCapacity = off + 5;
          byte[] bytes = this.bytes;
          if (minCapacity > bytes.length) {
              bytes = grow(minCapacity);
          }
          int off = this.off;
          if ((context.features & MASK_WRITE_BOOLEAN_AS_NUMBER) != 0) {
              bytes[off++] = (byte) (value ? '1' : '0');
          } else {
              off = IOUtils.putBoolean(bytes, off, value);
          }
          this.off = off;
      }
  
      @Override
      public final String toString() {
          return new String(bytes, 0, off, StandardCharsets.UTF_8);
      }
  
      @Override
      public final int flushTo(OutputStream out, Charset charset) throws IOException {
          if (off == 0) {
              return 0;
          }
  
          if (charset == null || charset == StandardCharsets.UTF_8 || charset == StandardCharsets.US_ASCII) {
              int len = off;
              out.write(bytes, 0, off);
              off = 0;
              return len;
          }
  
          if (charset == StandardCharsets.ISO_8859_1) {
              boolean hasNegative = false;
              if (METHOD_HANDLE_HAS_NEGATIVE != null) {
                  try {
                      hasNegative = (Boolean) METHOD_HANDLE_HAS_NEGATIVE.invoke(bytes, 0, bytes.length);
                  } catch (Throwable ignored) {
                      // ignored
                  }
              }
              if (!hasNegative) {
                  int len = off;
                  out.write(bytes, 0, off);
                  off = 0;
                  return len;
              }
          }
  
          String str = new String(bytes, 0, off);
          byte[] encodedBytes = str.getBytes(charset);
          out.write(encodedBytes);
          return encodedBytes.length;
      }
  }
  