content: package com.alibaba.fastjson2;
  
  import com.alibaba.fastjson2.util.IOUtils;
  import com.alibaba.fastjson2.util.JDKUtils;
  
  import java.math.BigDecimal;
  import java.math.BigInteger;
  import java.nio.charset.Charset;
  import java.nio.charset.StandardCharsets;
  import java.time.Instant;
  import java.time.LocalDate;
  import java.time.LocalDateTime;
  import java.util.HashMap;
  import java.util.Map;
  
  import static com.alibaba.fastjson2.JSONB.Constants.*;
  import static com.alibaba.fastjson2.JSONB.typeName;
  import static com.alibaba.fastjson2.util.JDKUtils.*;
  
  final class JSONBDump {
      static Charset GB18030;
  
      final byte[] bytes;
      final boolean raw;
      int offset;
      byte type;
  
      int strlen;
      byte strtype;
      int strBegin;
  
      String lastReference;
  
      final JSONWriter jsonWriter;
      final SymbolTable symbolTable;
  
      final Map<Integer, String> symbols = new HashMap<>();
  
      public JSONBDump(byte[] bytes, boolean raw) {
          this.bytes = bytes;
          this.raw = raw;
          jsonWriter = JSONWriter.ofPretty();
          this.symbolTable = null;
  
          dumpAny();
      }
  
      public JSONBDump(byte[] bytes, SymbolTable symbolTable, boolean raw) {
          this.bytes = bytes;
          this.raw = raw;
          this.symbolTable = symbolTable;
          jsonWriter = JSONWriter.ofPretty();
  
          dumpAny();
      }
  
      private void dumpAny() {
          if (offset >= bytes.length) {
              return;
          }
  
          type = bytes[offset++];
  
          switch (type) {
              case BC_NULL:
                  jsonWriter.writeNull();
                  return;
              case BC_TRUE:
                  jsonWriter.writeBool(true);
                  return;
              case BC_FALSE:
                  jsonWriter.writeBool(false);
                  return;
              case BC_DOUBLE_NUM_0:
                  jsonWriter.writeDouble(0);
                  return;
              case BC_DOUBLE_NUM_1:
                  jsonWriter.writeDouble(1);
                  return;
              case BC_INT8:
                  jsonWriter.writeInt8(bytes[offset++]);
                  return;
              case BC_INT16:
                  jsonWriter.writeInt16(
                          (short) (
                                  (bytes[offset++] << 8)
                                          + (bytes[offset++] & 0xFF)
                          )
                  );
                  return;
              case BC_TIMESTAMP_MINUTES:
              case BC_TIMESTAMP_SECONDS:
              case BC_INT32: {
                  int int32Value =
                          ((bytes[offset + 3] & 0xFF)) +
                                  ((bytes[offset + 2] & 0xFF) << 8) +
                                  ((bytes[offset + 1] & 0xFF) << 16) +
                                  ((bytes[offset]) << 24);
                  offset += 4;
                  jsonWriter.writeInt32(int32Value);
                  return;
              }
              case BC_INT64_INT: {
                  int int32Value =
                          ((bytes[offset + 3] & 0xFF)) +
                                  ((bytes[offset + 2] & 0xFF) << 8) +
                                  ((bytes[offset + 1] & 0xFF) << 16) +
                                  ((bytes[offset]) << 24);
                  offset += 4;
                  jsonWriter.writeInt64(int32Value);
                  return;
              }
              case BC_TIMESTAMP_MILLIS:
              case BC_INT64: {
                  long int64Value =
                          ((bytes[offset + 7] & 0xFFL)) +
                                  ((bytes[offset + 6] & 0xFFL) << 8) +
                                  ((bytes[offset + 5] & 0xFFL) << 16) +
                                  ((bytes[offset + 4] & 0xFFL) << 24) +
                                  ((bytes[offset + 3] & 0xFFL) << 32) +
                                  ((bytes[offset + 2] & 0xFFL) << 40) +
                                  ((bytes[offset + 1] & 0xFFL) << 48) +
                                  ((long) (bytes[offset]) << 56);
                  offset += 8;
                  jsonWriter.writeInt64(int64Value);
  
                  return;
              }
              case BC_BIGINT: {
                  int len = readInt32Value();
                  byte[] bytes = new byte[len];
                  System.arraycopy(this.bytes, offset, bytes, 0, len);
                  offset += len;
                  jsonWriter.writeBigInt(
                          new BigInteger(bytes));
                  return;
              }
              case BC_BIGINT_LONG: {
                  jsonWriter.writeInt64(
                          readInt64Value()
                  );
                  return;
              }
              case BC_FLOAT: {
                  int int32Value =
                          ((bytes[offset + 3] & 0xFF)) +
                                  ((bytes[offset + 2] & 0xFF) << 8) +
                                  ((bytes[offset + 1] & 0xFF) << 16) +
                                  ((bytes[offset]) << 24);
                  offset += 4;
                  jsonWriter.writeFloat(
                          Float.intBitsToFloat(int32Value));
                  return;
              }
              case BC_FLOAT_INT: {
                  jsonWriter.writeFloat(
                          readInt32Value()
                  );
                  return;
              }
              case BC_DOUBLE: {
                  long int64Value =
                          ((bytes[offset + 7] & 0xFFL)) +
                                  ((bytes[offset + 6] & 0xFFL) << 8) +
                                  ((bytes[offset + 5] & 0xFFL) << 16) +
                                  ((bytes[offset + 4] & 0xFFL) << 24) +
                                  ((bytes[offset + 3] & 0xFFL) << 32) +
                                  ((bytes[offset + 2] & 0xFFL) << 40) +
                                  ((bytes[offset + 1] & 0xFFL) << 48) +
                                  ((long) (bytes[offset]) << 56);
                  offset += 8;
                  jsonWriter.writeDouble(
                          Double.longBitsToDouble(int64Value));
                  return;
              }
              case BC_DOUBLE_LONG: {
                  jsonWriter.writeDouble(
                          readInt64Value()
                  );
                  return;
              }
              case BC_STR_UTF8: {
                  int strlen = readLength();
  
                  String str = new String(bytes, offset, strlen, StandardCharsets.UTF_8);
                  offset += strlen;
                  jsonWriter.writeString(str);
                  return;
              }
              case BC_CHAR: {
                  int intValue = readInt32Value();
                  jsonWriter.writeChar((char) intValue);
                  return;
              }
              case BC_STR_UTF16: {
                  int strlen = readLength();
                  String str = new String(bytes, offset, strlen, StandardCharsets.UTF_16);
                  offset += strlen;
                  jsonWriter.writeString(str);
                  return;
              }
              case BC_STR_UTF16LE: {
                  int strlen = readLength();
  
                  String str;
                  if (STRING_CREATOR_JDK11 != null && !JDKUtils.BIG_ENDIAN) {
                      byte[] chars = new byte[strlen];
                      System.arraycopy(bytes, offset, chars, 0, strlen);
                      str = STRING_CREATOR_JDK11.apply(chars, UTF16);
                  } else {
                      str = new String(bytes, offset, strlen, StandardCharsets.UTF_16LE);
                  }
  
                  offset += strlen;
                  jsonWriter.writeString(str);
                  return;
              }
              case BC_STR_UTF16BE: {
                  int strlen = readLength();
  
                  String str;
                  if (STRING_CREATOR_JDK11 != null && JDKUtils.BIG_ENDIAN) {
                      byte[] chars = new byte[strlen];
                      System.arraycopy(bytes, offset, chars, 0, strlen);
                      str = STRING_CREATOR_JDK11.apply(chars, UTF16);
                  } else {
                      str = new String(bytes, offset, strlen, StandardCharsets.UTF_16BE);
                  }
  
                  offset += strlen;
                  jsonWriter.writeString(str);
                  return;
              }
              case BC_STR_GB18030: {
                  if (GB18030 == null) {
                      GB18030 = Charset.forName("GB18030");
                  }
  
                  int strlen = readLength();
                  String str = new String(bytes, offset, strlen, GB18030);
                  offset += strlen;
                  jsonWriter.writeString(str);
                  return;
              }
              case BC_SYMBOL: {
                  int symbol;
                  if (isInt()) {
                      symbol = readInt32Value();
                      if (raw) {
                          jsonWriter.writeString("#" + symbol);
                      } else {
                          String name = getString(symbol);
                          jsonWriter.writeString(name);
                      }
                  } else {
                      String name = readString();
                      symbol = readInt32Value();
                      symbols.put(symbol, name);
                      if (raw) {
                          jsonWriter.writeString(name + "#" + symbol);
                      } else {
                          jsonWriter.writeString(name);
                      }
                  }
                  return;
              }
              case BC_DECIMAL: {
                  int scale = readInt32Value();
                  BigInteger unscaledValue;
                  int type = bytes[offset++];
                  switch (type) {
                      case BC_BIGINT_LONG:
                          unscaledValue = BigInteger.valueOf(
                                  readInt64Value()
                          );
                          break;
                      case BC_INT32:
                          unscaledValue = BigInteger.valueOf(
                                  readInt32Value()
                          );
                          break;
                      case BC_INT64:
                          unscaledValue = BigInteger.valueOf(
                                  IOUtils.getLongBE(bytes, offset)
                          );
                          offset += 8;
                          break;
                      default:
                          if (type >= BC_INT32_NUM_MIN && type <= BC_INT32_NUM_MAX) {
                              unscaledValue = BigInteger.valueOf(type);
                          } else if (type >= BC_INT32_BYTE_MIN && type <= BC_INT32_BYTE_MAX) {
                              unscaledValue = BigInteger.valueOf(((type - BC_INT32_BYTE_ZERO) << 8)
                                      + (bytes[offset++] & 0xFF));
                          } else if (type >= BC_INT32_SHORT_MIN && type <= BC_INT32_SHORT_MAX) {
                              unscaledValue = BigInteger.valueOf(((type - BC_INT32_SHORT_ZERO) << 16)
                                      + ((bytes[offset++] & 0xFF) << 8)
                                      + (bytes[offset++] & 0xFF));
                          } else {
                              int len = readInt32Value();
                              byte[] bytes = new byte[len];
                              System.arraycopy(this.bytes, offset, bytes, 0, len);
                              offset += len;
                              unscaledValue = new BigInteger(bytes);
                          }
                          break;
                  }
  
                  BigDecimal decimal;
                  if (scale == 0) {
                      decimal = new BigDecimal(unscaledValue);
                  } else {
                      decimal = new BigDecimal(unscaledValue, scale);
                  }
                  jsonWriter.writeDecimal(decimal, 0, null);
                  return;
              }
              case BC_DECIMAL_LONG: {
                  jsonWriter.writeDecimal(
                          BigDecimal.valueOf(
                                  readInt64Value()
                          ),
                          0,
                          null
                  );
                  return;
              }
              case BC_TYPED_ANY: {
                  boolean isInt = isInt();
                  int symbol;
                  String typeName = null;
                  if (isInt) {
                      symbol = readInt32Value();
                  } else {
                      typeName = readString();
                      symbol = readInt32Value();
                      symbols.put(symbol, typeName);
                  }
  
                  if (!raw && bytes[offset] == BC_OBJECT) {
                      if (typeName == null) {
                          typeName = getString(symbol);
                      }
                      offset++;
                      dumpObject(typeName);
                      return;
                  }
  
                  jsonWriter.startObject();
  
                  jsonWriter.writeName("@type");
                  jsonWriter.writeColon();
                  if (typeName == null) {
                      if (symbol < 0) {
                          if (raw) {
                              jsonWriter.writeString("#" + symbol);
                          } else {
                              String name = symbolTable.getName(-symbol);
                              jsonWriter.writeString(name);
                          }
                      } else {
                          jsonWriter.writeString("#" + symbol);
                      }
                  } else {
                      if (raw) {
                          jsonWriter.writeString(typeName + "#" + symbol);
                      } else {
                          jsonWriter.writeString(typeName);
                      }
                  }
  
                  jsonWriter.writeName("@value");
                  jsonWriter.writeColon();
                  dumpAny();
  
                  jsonWriter.endObject();
                  return;
              }
              case BC_BINARY: {
                  int len = readInt32Value();
                  byte[] bytes = new byte[len];
                  System.arraycopy(this.bytes, offset, bytes, 0, len);
                  offset += len;
                  jsonWriter.writeBinary(bytes);
                  return;
              }
              case BC_REFERENCE: {
                  dumpReference();
                  break;
              }
              case BC_LOCAL_DATETIME: {
                  int year = (bytes[offset++] << 8) + (bytes[offset++] & 0xFF);
                  int month = bytes[offset++];
                  int dayOfMonth = bytes[offset++];
                  int hour = bytes[offset++];
                  int minute = bytes[offset++];
                  int second = bytes[offset++];
  
                  int nano = readInt32Value();
  
                  LocalDateTime localDateTime = LocalDateTime.of(year, month, dayOfMonth, hour, minute, second, nano);
                  jsonWriter.writeLocalDateTime(localDateTime);
                  break;
              }
              case BC_LOCAL_DATE: {
                  int year = (bytes[offset++] << 8) + (bytes[offset++] & 0xFF);
                  int month = bytes[offset++];
                  int dayOfMonth = bytes[offset++];
  
                  LocalDate localDate = LocalDate.of(year, month, dayOfMonth);
                  jsonWriter.writeLocalDate(localDate);
                  break;
              }
              case BC_TIMESTAMP:
                  long epochSeconds = readInt64Value();
                  int nano = readInt32Value();
                  jsonWriter.writeInstant(
                          Instant.ofEpochSecond(epochSeconds, nano)
                  );
                  break;
              case BC_OBJECT: {
                  dumpObject(null);
                  break;
              }
              default:
                  if (type >= BC_INT32_NUM_MIN && type <= BC_INT32_NUM_MAX) {
                      jsonWriter.writeInt32(type);
                      return;
                  }
  
                  if (type >= BC_INT64_NUM_MIN && type <= BC_INT64_NUM_MAX) {
                      long value = INT64_NUM_LOW_VALUE + (type - BC_INT64_NUM_MIN);
                      jsonWriter.writeInt64(value);
                      return;
                  }
  
                  if (type >= BC_INT32_BYTE_MIN && type <= BC_INT32_BYTE_MAX) {
                      int value = ((type - BC_INT32_BYTE_ZERO) << 8)
                              + (bytes[offset++] & 0xFF);
                      jsonWriter.writeInt32(value);
                      return;
                  }
  
                  if (type >= BC_INT32_SHORT_MIN && type <= BC_INT32_SHORT_MAX) {
                      int value = ((type - BC_INT32_SHORT_ZERO) << 16)
                              + ((bytes[offset++] & 0xFF) << 8)
                              + (bytes[offset++] & 0xFF);
                      jsonWriter.writeInt32(value);
                      return;
                  }
  
                  if (type >= BC_INT64_BYTE_MIN && type <= BC_INT64_BYTE_MAX) {
                      int value = ((type - BC_INT64_BYTE_ZERO) << 8)
                              + (bytes[offset++] & 0xFF);
                      jsonWriter.writeInt32(value);
                      return;
                  }
  
                  if (type >= BC_INT64_SHORT_MIN && type <= BC_INT64_SHORT_MAX) {
                      int value = ((type - BC_INT64_SHORT_ZERO) << 16)
                              + ((bytes[offset++] & 0xFF) << 8)
                              + (bytes[offset++] & 0xFF);
                      jsonWriter.writeInt64(value);
                      return;
                  }
  
                  if (type >= BC_ARRAY_FIX_MIN && type <= BC_ARRAY) {
                      dumpArray();
                      return;
                  }
  
                  if (type >= BC_STR_ASCII_FIX_MIN) {
                      strlen = type == BC_STR_ASCII
                              ? readLength()
                              : type - BC_STR_ASCII_FIX_MIN;
  
                      if (strlen < 0) {
                          jsonWriter.writeRaw("{\"$symbol\":");
                          jsonWriter.writeInt32(strlen);
                          jsonWriter.writeRaw("}");
                          return;
                      }
  
                      String str = new String(bytes, offset, strlen, StandardCharsets.ISO_8859_1);
                      offset += strlen;
                      jsonWriter.writeString(str);
                      return;
                  }
  
                  throw new JSONException("not support type : " + typeName(type) + ", offset " + offset);
          }
      }
  
      private void dumpArray() {
          int len = type == BC_ARRAY
                  ? readLength()
                  : type - BC_ARRAY_FIX_MIN;
  
          if (len == 0) {
              jsonWriter.writeRaw("[]");
              return;
          }
  
          if (len == 1) {
              type = bytes[offset];
              if (isInt() || type == BC_NULL || (type >= BC_STR_ASCII_FIX_MIN && type <= BC_STR_ASCII_FIX_MAX)) {
                  jsonWriter.writeRaw("[");
                  dumpAny();
                  jsonWriter.writeRaw("]");
                  return;
              }
          }
  
          jsonWriter.startArray();
          for (int i = 0; i < len; ++i) {
              if (i != 0) {
                  jsonWriter.writeComma();
              }
  
              if (isReference()) {
                  dumpReference();
                  continue;
              }
  
              dumpAny();
          }
          jsonWriter.endArray();
      }
  
      private void dumpObject(String typeName) {
          if (typeName != null) {
              jsonWriter.startObject();
              jsonWriter.writeName("@type");
              jsonWriter.writeColon();
              jsonWriter.writeString(typeName);
          } else {
              if (bytes[offset] == BC_OBJECT_END) {
                  jsonWriter.writeRaw("{}");
                  offset++;
                  return;
              }
              jsonWriter.startObject();
          }
  
          _for:
          for (int valueCount = 0; ; ) {
              byte type = bytes[offset];
              switch (type) {
                  case BC_OBJECT_END:
                      offset++;
                      break _for;
                  case BC_REFERENCE: {
                      dumpReference();
                      break;
                  }
                  case BC_SYMBOL: {
                      offset++;
                      if (isInt()) {
                          int symbol = readInt32Value();
                          if (raw) {
                              jsonWriter.writeName("#" + symbol);
                          } else {
                              jsonWriter.writeName(
                                      getString(symbol)
                              );
                          }
                      } else {
                          String name = readString();
                          int symbol = readInt32Value();
  
                          symbols.put(symbol, name);
                          if (raw) {
                              jsonWriter.writeName(name + "#" + symbol);
                          } else {
                              jsonWriter.writeName(name);
                          }
                      }
                      break;
                  }
                  default:
                      if (isString()) {
                          jsonWriter.writeName(readString());
                      } else {
                          if (type >= BC_INT32_NUM_MIN && type <= BC_INT32) {
                              jsonWriter.writeName(
                                      readInt32Value()
                              );
                          } else if ((type >= BC_INT64_NUM_MIN && type <= BC_INT64_NUM_MAX)
                                  || type == BC_INT64
                          ) {
                              jsonWriter.writeName(
                                      readInt64Value()
                              );
                          } else {
                              if (valueCount != 0) {
                                  jsonWriter.writeComma();
                              }
  
                              dumpAny();
                          }
                      }
                      break;
              }
  
              valueCount++;
              jsonWriter.writeColon();
  
              if (isReference()) {
                  dumpReference();
                  continue;
              }
  
              dumpAny();
          }
  
          jsonWriter.endObject();
      }
  
      private void dumpReference() {
          jsonWriter.writeRaw("{\"$ref\":");
          String reference = readReference();
          jsonWriter.writeString(reference);
          if (!"#-1".equals(reference)) {
              lastReference = reference;
          }
          jsonWriter.writeRaw("}");
      }
  
      int readInt32Value() {
          byte type = bytes[offset++];
          if (type >= BC_INT32_NUM_MIN && type <= BC_INT32_NUM_MAX) {
              return type;
          }
  
          if (type >= BC_INT32_BYTE_MIN && type <= BC_INT32_BYTE_MAX) {
              return ((type - BC_INT32_BYTE_ZERO) << 8)
                      + (bytes[offset++] & 0xFF);
          }
  
          if (type >= BC_INT32_SHORT_MIN && type <= BC_INT32_SHORT_MAX) {
              return ((type - BC_INT32_SHORT_ZERO) << 16)
                      + ((bytes[offset++] & 0xFF) << 8)
                      + (bytes[offset++] & 0xFF);
          }
  
          switch (type) {
              case BC_INT32:
              case BC_TIMESTAMP_MINUTES:
              case BC_TIMESTAMP_SECONDS:
                  int int32Value =
                          ((bytes[offset + 3] & 0xFF)) +
                                  ((bytes[offset + 2] & 0xFF) << 8) +
                                  ((bytes[offset + 1] & 0xFF) << 16) +
                                  ((bytes[offset]) << 24);
                  offset += 4;
                  return int32Value;
              default:
                  break;
          }
          throw new JSONException("readInt32Value not support " + typeName(type) + ", offset " + offset + "/" + bytes.length);
      }
  
      long readInt64Value() {
          byte type = bytes[offset++];
          if (type >= BC_INT32_NUM_MIN && type <= BC_INT32_NUM_MAX) {
              return type;
          }
  
          if (type >= BC_INT32_BYTE_MIN && type <= BC_INT32_BYTE_MAX) {
              return ((type - BC_INT32_BYTE_ZERO) << 8)
                      + (bytes[offset++] & 0xFF);
          }
  
          if (type >= BC_INT32_SHORT_MIN && type <= BC_INT32_SHORT_MAX) {
              return ((type - BC_INT32_SHORT_ZERO) << 16)
                      + ((bytes[offset++] & 0xFF) << 8)
                      + (bytes[offset++] & 0xFF);
          }
  
          if (type >= BC_INT64_NUM_MIN && type <= BC_INT64_NUM_MAX) {
              return (long) INT64_NUM_LOW_VALUE + (type - BC_INT64_NUM_MIN);
          }
  
          if (type >= BC_INT64_BYTE_MIN && type <= BC_INT64_BYTE_MAX) {
              return ((type - BC_INT64_BYTE_ZERO) << 8)
                      + (bytes[offset++] & 0xFF);
          }
  
          if (type >= BC_INT64_SHORT_MIN && type <= BC_INT64_SHORT_MAX) {
              return ((type - BC_INT64_SHORT_ZERO) << 16)
                      + ((bytes[offset++] & 0xFF) << 8)
                      + (bytes[offset++] & 0xFF);
          }
  
          switch (type) {
              case BC_INT8:
                  return bytes[offset++];
              case BC_INT16:
                  int int16Value =
                          ((bytes[offset + 1] & 0xFF) +
                                  (bytes[offset] << 8));
                  offset += 2;
                  return int16Value;
              case BC_INT32:
              case BC_INT64_INT:
                  int int32Value =
                          ((bytes[offset + 3] & 0xFF)) +
                                  ((bytes[offset + 2] & 0xFF) << 8) +
                                  ((bytes[offset + 1] & 0xFF) << 16) +
                                  ((bytes[offset]) << 24);
                  offset += 4;
                  return int32Value;
              case BC_TIMESTAMP_MILLIS:
              case BC_INT64:
                  long int64Value =
                          ((bytes[offset + 7] & 0xFFL)) +
                                  ((bytes[offset + 6] & 0xFFL) << 8) +
                                  ((bytes[offset + 5] & 0xFFL) << 16) +
                                  ((bytes[offset + 4] & 0xFFL) << 24) +
                                  ((bytes[offset + 3] & 0xFFL) << 32) +
                                  ((bytes[offset + 2] & 0xFFL) << 40) +
                                  ((bytes[offset + 1] & 0xFFL) << 48) +
                                  ((long) (bytes[offset]) << 56);
                  offset += 8;
                  return int64Value;
              default:
                  break;
          }
          throw new JSONException("readInt64Value not support " + typeName(type) + ", offset " + offset + "/" + bytes.length);
      }
  
      int readLength() {
          byte type = bytes[offset++];
          if (type >= BC_INT32_NUM_MIN && type <= BC_INT32_NUM_MAX) {
              return type;
          }
  
          if (type >= BC_INT32_SHORT_MIN && type <= BC_INT32_SHORT_MAX) {
              return ((type - BC_INT32_SHORT_ZERO) << 16)
                      + ((bytes[offset++] & 0xFF) << 8)
                      + (bytes[offset++] & 0xFF);
          }
  
          if (type >= BC_INT32_BYTE_MIN && type <= BC_INT32_BYTE_MAX) {
              return ((type - BC_INT32_BYTE_ZERO) << 8)
                      + (bytes[offset++] & 0xFF);
          }
  
          if (type == BC_INT32) {
              return (bytes[offset++] << 24)
                      + ((bytes[offset++] & 0xFF) << 16)
                      + ((bytes[offset++] & 0xFF) << 8)
                      + (bytes[offset++] & 0xFF);
          }
  
          throw new JSONException("not support length type : " + type);
      }
  
      boolean isReference() {
          return offset < bytes.length && bytes[offset] == BC_REFERENCE;
      }
  
      boolean isString() {
          byte type = bytes[offset];
          return type >= BC_STR_ASCII_FIX_MIN && type <= BC_STR_UTF16BE;
      }
  
      String readString() {
          strtype = bytes[offset++];
  
          strBegin = offset;
          Charset charset;
          if (strtype >= BC_STR_ASCII_FIX_MIN && strtype <= BC_STR_ASCII) {
              if (strtype == BC_STR_ASCII) {
                  strlen = readLength();
                  strBegin = offset;
              } else {
                  strlen = strtype - BC_STR_ASCII_FIX_MIN;
              }
  
              charset = StandardCharsets.ISO_8859_1;
          } else if (strtype == BC_STR_UTF8) {
              strlen = readLength();
              strBegin = offset;
  
              charset = StandardCharsets.UTF_8;
          } else if (strtype == BC_STR_UTF16) {
              strlen = readLength();
              strBegin = offset;
              charset = StandardCharsets.UTF_16;
          } else if (strtype == BC_STR_UTF16LE) {
              strlen = readLength();
              strBegin = offset;
  
              if (STRING_CREATOR_JDK11 != null && !JDKUtils.BIG_ENDIAN) {
                  byte[] chars = new byte[strlen];
                  System.arraycopy(bytes, offset, chars, 0, strlen);
                  String str = STRING_CREATOR_JDK11.apply(chars, UTF16);
                  offset += strlen;
                  return str;
              }
  
              charset = StandardCharsets.UTF_16LE;
          } else if (strtype == BC_STR_UTF16BE) {
              strlen = readLength();
              strBegin = offset;
  
              if (STRING_CREATOR_JDK11 != null && JDKUtils.BIG_ENDIAN) {
                  byte[] chars = new byte[strlen];
                  System.arraycopy(bytes, offset, chars, 0, strlen);
                  String str = STRING_CREATOR_JDK11.apply(chars, UTF16);
                  offset += strlen;
                  return str;
              }
  
              charset = StandardCharsets.UTF_16BE;
          } else {
              throw new JSONException("readString not support type " + typeName(strtype) + ", offset " + offset + "/" + bytes.length);
          }
  
          if (strlen < 0) {
              return symbolTable.getName(-strlen);
          }
  
          String str = new String(bytes, offset, strlen, charset);
          offset += strlen;
          return str;
      }
  
      String readReference() {
          if (bytes[offset] != BC_REFERENCE) {
              return null;
          }
          offset++;
          if (isString()) {
              return readString();
          }
  
          throw new JSONException("reference not support input " + typeName(type));
      }
  
      @Override
      public String toString() {
          return jsonWriter.toString();
      }
  
      boolean isInt() {
          int type = bytes[offset];
          return (type >= BC_BIGINT_LONG && type <= BC_INT32)
                  || type == BC_TIMESTAMP_MINUTES
                  || type == BC_TIMESTAMP_SECONDS
                  || type == BC_TIMESTAMP_MILLIS;
      }
  
      public String getString(int symbol) {
          String name;
          if (symbol < 0) {
              name = symbolTable.getName(-symbol);
          } else {
              name = symbols.get(symbol);
          }
  
          if (name == null) {
              throw new JSONException("symbol not found : " + symbol);
          }
          return name;
      }
  }
  