content: /*
   * Copyright 1999-2017 Alibaba Group.
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *      http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  package com.alibaba.fastjson.util;
  
  import java.io.Closeable;
  import java.io.InputStream;
  import java.io.Reader;
  import java.nio.ByteBuffer;
  import java.nio.CharBuffer;
  import java.nio.charset.CharacterCodingException;
  import java.nio.charset.Charset;
  import java.nio.charset.CharsetDecoder;
  import java.nio.charset.CoderResult;
  import java.nio.charset.MalformedInputException;
  import java.security.AccessController;
  import java.security.PrivilegedAction;
  import java.util.Arrays;
  import java.util.Properties;
  
  import com.alibaba.fastjson.JSONException;
  
  /**
   * @author wenshao[szujobs@hotmail.com]
   */
  public class IOUtils {
      
      public final static String     FASTJSON_PROPERTIES              = "fastjson.properties";
      public final static String     FASTJSON_COMPATIBLEWITHJAVABEAN  = "fastjson.compatibleWithJavaBean";
      public final static String     FASTJSON_COMPATIBLEWITHFIELDNAME = "fastjson.compatibleWithFieldName";
      public final static Properties DEFAULT_PROPERTIES               = new Properties();
      public final static Charset    UTF8                             = Charset.forName("UTF-8");
      public final static char[]     DIGITS                           = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
      public final static boolean[]  firstIdentifierFlags             = new boolean[256];
      public final static boolean[]  identifierFlags                  = new boolean[256];
      static {
          for (char c = 0; c < firstIdentifierFlags.length; ++c) {
              if (c >= 'A' && c <= 'Z') {
                  firstIdentifierFlags[c] = true;
              } else if (c >= 'a' && c <= 'z') {
                  firstIdentifierFlags[c] = true;
              } else if (c == '_' || c == '$') {
                  firstIdentifierFlags[c] = true;
              }
          }
  
          for (char c = 0; c < identifierFlags.length; ++c) {
              if (c >= 'A' && c <= 'Z') {
                  identifierFlags[c] = true;
              } else if (c >= 'a' && c <= 'z') {
                  identifierFlags[c] = true;
              } else if (c == '_') {
                  identifierFlags[c] = true;
              } else if (c >= '0' && c <= '9') {
                  identifierFlags[c] = true;
              }
          }
  
          try {
              loadPropertiesFromFile();
          } catch (Throwable e) {
              //skip
          }
      }
      
      public static String getStringProperty(String name) {
          String prop = null;
          try {
              prop = System.getProperty(name);
          } catch (SecurityException e) {
              //skip
          }
          return (prop == null) ? DEFAULT_PROPERTIES.getProperty(name) : prop;
      }
      
      public static void loadPropertiesFromFile(){
          InputStream imputStream = AccessController.doPrivileged(new PrivilegedAction<InputStream>() {
              public InputStream run() {
                  ClassLoader cl = Thread.currentThread().getContextClassLoader();
                  if (cl != null) {
                      return cl.getResourceAsStream(FASTJSON_PROPERTIES);
                  } else {
                      return ClassLoader.getSystemResourceAsStream(FASTJSON_PROPERTIES);
                  }
              }
          });
          
          if (null != imputStream) {
              try {
                  DEFAULT_PROPERTIES.load(imputStream);
                  imputStream.close();
              } catch (java.io.IOException e) {
                  // skip
              }
          }
      }
  
      public final static byte[]    specicalFlags_doubleQuotes = new byte[161];
      public final static byte[]    specicalFlags_singleQuotes = new byte[161];
      public final static boolean[] specicalFlags_doubleQuotesFlags = new boolean[161];
      public final static boolean[] specicalFlags_singleQuotesFlags = new boolean[161];
  
      public final static char[]    replaceChars               = new char[93];
      static {
          specicalFlags_doubleQuotes['\0'] = 4;
          specicalFlags_doubleQuotes['\1'] = 4;
          specicalFlags_doubleQuotes['\2'] = 4;
          specicalFlags_doubleQuotes['\3'] = 4;
          specicalFlags_doubleQuotes['\4'] = 4;
          specicalFlags_doubleQuotes['\5'] = 4;
          specicalFlags_doubleQuotes['\6'] = 4;
          specicalFlags_doubleQuotes['\7'] = 4;
          specicalFlags_doubleQuotes['\b'] = 1; // 8
          specicalFlags_doubleQuotes['\t'] = 1; // 9
          specicalFlags_doubleQuotes['\n'] = 1; // 10
          specicalFlags_doubleQuotes['\u000B'] = 4; // 11
          specicalFlags_doubleQuotes['\f'] = 1; // 12
          specicalFlags_doubleQuotes['\r'] = 1; // 13
          specicalFlags_doubleQuotes['\"'] = 1; // 34
          specicalFlags_doubleQuotes['\\'] = 1; // 92
  
          specicalFlags_singleQuotes['\0'] = 4;
          specicalFlags_singleQuotes['\1'] = 4;
          specicalFlags_singleQuotes['\2'] = 4;
          specicalFlags_singleQuotes['\3'] = 4;
          specicalFlags_singleQuotes['\4'] = 4;
          specicalFlags_singleQuotes['\5'] = 4;
          specicalFlags_singleQuotes['\6'] = 4;
          specicalFlags_singleQuotes['\7'] = 4;
          specicalFlags_singleQuotes['\b'] = 1; // 8
          specicalFlags_singleQuotes['\t'] = 1; // 9
          specicalFlags_singleQuotes['\n'] = 1; // 10
          specicalFlags_singleQuotes['\u000B'] = 4; // 11
          specicalFlags_singleQuotes['\f'] = 1; // 12
          specicalFlags_singleQuotes['\r'] = 1; // 13
          specicalFlags_singleQuotes['\\'] = 1; // 92
          specicalFlags_singleQuotes['\''] = 1; // 39
  
          for (int i = 14; i <= 31; ++i) {
              specicalFlags_doubleQuotes[i] = 4;
              specicalFlags_singleQuotes[i] = 4;
          }
  
          for (int i = 127; i < 160; ++i) {
              specicalFlags_doubleQuotes[i] = 4;
              specicalFlags_singleQuotes[i] = 4;
          }
          
          for (int i = 0; i < 161; ++i) {
              specicalFlags_doubleQuotesFlags[i] = specicalFlags_doubleQuotes[i] != 0;
              specicalFlags_singleQuotesFlags[i] = specicalFlags_singleQuotes[i] != 0;
          }
  
          replaceChars['\0'] = '0';
          replaceChars['\1'] = '1';
          replaceChars['\2'] = '2';
          replaceChars['\3'] = '3';
          replaceChars['\4'] = '4';
          replaceChars['\5'] = '5';
          replaceChars['\6'] = '6';
          replaceChars['\7'] = '7';
          replaceChars['\b'] = 'b'; // 8
          replaceChars['\t'] = 't'; // 9
          replaceChars['\n'] = 'n'; // 10
          replaceChars['\u000B'] = 'v'; // 11
          replaceChars['\f'] = 'f'; // 12
          replaceChars['\r'] = 'r'; // 13
          replaceChars['\"'] = '"'; // 34
          replaceChars['\''] = '\''; // 39
          replaceChars['/'] = '/'; // 47
          replaceChars['\\'] = '\\'; // 92
      }
  
      public final static char[]    ASCII_CHARS                = { '0', '0', '0', '1', '0', '2', '0', '3', '0', '4', '0',
              '5', '0', '6', '0', '7', '0', '8', '0', '9', '0', 'A', '0', 'B', '0', 'C', '0', 'D', '0', 'E', '0', 'F',
              '1', '0', '1', '1', '1', '2', '1', '3', '1', '4', '1', '5', '1', '6', '1', '7', '1', '8', '1', '9', '1',
              'A', '1', 'B', '1', 'C', '1', 'D', '1', 'E', '1', 'F', '2', '0', '2', '1', '2', '2', '2', '3', '2', '4',
              '2', '5', '2', '6', '2', '7', '2', '8', '2', '9', '2', 'A', '2', 'B', '2', 'C', '2', 'D', '2', 'E', '2',
              'F',                                            };
  
      public static void close(Closeable x) {
          if (x != null) {
              try {
                  x.close();
              } catch (Exception e) {
                  // skip
              }
          }
      }
  
      // Requires positive x
      public static int stringSize(long x) {
          long p = 10;
          for (int i = 1; i < 19; i++) {
              if (x < p) return i;
              p = 10 * p;
          }
          return 19;
      }
  
      public static void getChars(long i, int index, char[] buf) {
          long q;
          int r;
          int charPos = index;
          char sign = 0;
  
          if (i < 0) {
              sign = '-';
              i = -i;
          }
  
          // Get 2 digits/iteration using longs until quotient fits into an int
          while (i > Integer.MAX_VALUE) {
              q = i / 100;
              // really: r = i - (q * 100);
              r = (int) (i - ((q << 6) + (q << 5) + (q << 2)));
              i = q;
              buf[--charPos] = DigitOnes[r];
              buf[--charPos] = DigitTens[r];
          }
  
          // Get 2 digits/iteration using ints
          int q2;
          int i2 = (int) i;
          while (i2 >= 65536) {
              q2 = i2 / 100;
              // really: r = i2 - (q * 100);
              r = i2 - ((q2 << 6) + (q2 << 5) + (q2 << 2));
              i2 = q2;
              buf[--charPos] = DigitOnes[r];
              buf[--charPos] = DigitTens[r];
          }
  
          // Fall thru to fast mode for smaller numbers
          // assert(i2 <= 65536, i2);
          for (;;) {
              q2 = (i2 * 52429) >>> (16 + 3);
              r = i2 - ((q2 << 3) + (q2 << 1)); // r = i2-(q2*10) ...
              buf[--charPos] = digits[r];
              i2 = q2;
              if (i2 == 0) break;
          }
          if (sign != 0) {
              buf[--charPos] = sign;
          }
      }
  
      /**
       * Places characters representing the integer i into the character array buf. The characters are placed into the
       * buffer backwards starting with the least significant digit at the specified index (exclusive), and working
       * backwards from there. Will fail if i == Integer.MIN_VALUE
       */
      public static void getChars(int i, int index, char[] buf) {
          int q, r, p = index;
          char sign = 0;
  
          if (i < 0) {
              sign = '-';
              i = -i;
          }
  
          while (i >= 65536) {
              q = i / 100;
              // really: r = i - (q * 100);
              r = i - ((q << 6) + (q << 5) + (q << 2));
              i = q;
              buf[--p] = DigitOnes[r];
              buf[--p] = DigitTens[r];
          }
  
          // Fall thru to fast mode for smaller numbers
          // assert(i <= 65536, i);
          for (;;) {
              q = (i * 52429) >>> (16 + 3);
              r = i - ((q << 3) + (q << 1)); // r = i-(q*10) ...
              buf[--p] = digits[r];
              i = q;
              if (i == 0) break;
          }
          if (sign != 0) {
              buf[--p] = sign;
          }
      }
  
      public static void getChars(byte b, int index, char[] buf) {
          int i = b;
          int q, r;
          int charPos = index;
          char sign = 0;
  
          if (i < 0) {
              sign = '-';
              i = -i;
          }
  
          // Fall thru to fast mode for smaller numbers
          // assert(i <= 65536, i);
          for (;;) {
              q = (i * 52429) >>> (16 + 3);
              r = i - ((q << 3) + (q << 1)); // r = i-(q*10) ...
              buf[--charPos] = digits[r];
              i = q;
              if (i == 0) break;
          }
          if (sign != 0) {
              buf[--charPos] = sign;
          }
      }
  
      /**
       * All possible chars for representing a number as a String
       */
      final static char[] digits    = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f',
              'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z' };
  
      final static char[] DigitTens = { '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '1', '1', '1', '1', '1', '1',
              '1', '1', '1', '1', '2', '2', '2', '2', '2', '2', '2', '2', '2', '2', '3', '3', '3', '3', '3', '3', '3',
              '3', '3', '3', '4', '4', '4', '4', '4', '4', '4', '4', '4', '4', '5', '5', '5', '5', '5', '5', '5', '5',
              '5', '5', '6', '6', '6', '6', '6', '6', '6', '6', '6', '6', '7', '7', '7', '7', '7', '7', '7', '7', '7',
              '7', '8', '8', '8', '8', '8', '8', '8', '8', '8', '8', '9', '9', '9', '9', '9', '9', '9', '9', '9', '9', };
  
      final static char[] DigitOnes = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5',
              '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6',
              '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7',
              '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8',
              '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', };
  
      final static int[]  sizeTable = { 9, 99, 999, 9999, 99999, 999999, 9999999, 99999999, 999999999, Integer.MAX_VALUE };
  
      // Requires positive x
      public static int stringSize(int x) {
          for (int i = 0;; i++) {
              if (x <= sizeTable[i]) {
                  return i + 1;
              }
          }
      }
  
      public static void decode(CharsetDecoder charsetDecoder, ByteBuffer byteBuf, CharBuffer charByte) {
          try {
              CoderResult cr = charsetDecoder.decode(byteBuf, charByte, true);
  
              if (!cr.isUnderflow()) {
                  cr.throwException();
              }
  
              cr = charsetDecoder.flush(charByte);
  
              if (!cr.isUnderflow()) {
                  cr.throwException();
              }
          } catch (CharacterCodingException x) {
              // Substitution is always enabled,
              // so this shouldn't happen
              throw new JSONException("utf8 decode error, " + x.getMessage(), x);
          }
      }
  
      public static boolean firstIdentifier(char ch) {
          return ch < IOUtils.firstIdentifierFlags.length && IOUtils.firstIdentifierFlags[ch];
      }
      
      public static boolean isIdent(char ch) {
          return ch < identifierFlags.length && identifierFlags[ch];
      }
      
      public static final char[] CA = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".toCharArray();
      public static final int[]  IA = new int[256];
      static {
          Arrays.fill(IA, -1);
          for (int i = 0, iS = CA.length; i < iS; i++)
              IA[CA[i]] = i;
          IA['='] = 0;
      }
  
      /**
       * Decodes a BASE64 encoded char array that is known to be resonably well formatted. The method is about twice as
       * fast as #decode(char[]). The preconditions are:<br>
       * + The array must have a line length of 76 chars OR no line separators at all (one line).<br>
       * + Line separator must be "\r\n", as specified in RFC 2045 + The array must not contain illegal characters within
       * the encoded string<br>
       * + The array CAN have illegal characters at the beginning and end, those will be dealt with appropriately.<br>
       *
       * @author Mikael Grev Date: 2004-aug-02 Time: 11:31:11
       * @param chars The source array. Length 0 will return an empty array. <code>null</code> will throw an exception.
       * @return The decoded array of bytes. May be of length 0.
       */
      public static byte[] decodeBase64(char[] chars, int offset, int charsLen) {
          // Check special case
          if (charsLen == 0) {
              return new byte[0];
          }
  
          int sIx = offset, eIx = offset + charsLen - 1; // Start and end index after trimming.
  
          // Trim illegal chars from start
          while (sIx < eIx && IA[chars[sIx]] < 0)
              sIx++;
  
          // Trim illegal chars from end
          while (eIx > 0 && IA[chars[eIx]] < 0)
              eIx--;
  
          // get the padding count (=) (0, 1 or 2)
          int pad = chars[eIx] == '=' ? (chars[eIx - 1] == '=' ? 2 : 1) : 0; // Count '=' at end.
          int cCnt = eIx - sIx + 1; // Content count including possible separators
          int sepCnt = charsLen > 76 ? (chars[76] == '\r' ? cCnt / 78 : 0) << 1 : 0;
  
          int len = ((cCnt - sepCnt) * 6 >> 3) - pad; // The number of decoded bytes
          byte[] bytes = new byte[len]; // Preallocate byte[] of exact length
  
          // Decode all but the last 0 - 2 bytes.
          int d = 0;
          for (int cc = 0, eLen = (len / 3) * 3; d < eLen;) {
              // Assemble three bytes into an int from four "valid" characters.
              int i = IA[chars[sIx++]] << 18 | IA[chars[sIx++]] << 12 | IA[chars[sIx++]] << 6 | IA[chars[sIx++]];
  
              // Add the bytes
              bytes[d++] = (byte) (i >> 16);
              bytes[d++] = (byte) (i >> 8);
              bytes[d++] = (byte) i;
  
              // If line separator, jump over it.
              if (sepCnt > 0 && ++cc == 19) {
                  sIx += 2;
                  cc = 0;
              }
          }
  
          if (d < len) {
              // Decode last 1-3 bytes (incl '=') into 1-3 bytes
              int i = 0;
              for (int j = 0; sIx <= eIx - pad; j++)
                  i |= IA[chars[sIx++]] << (18 - j * 6);
  
              for (int r = 16; d < len; r -= 8)
                  bytes[d++] = (byte) (i >> r);
          }
  
          return bytes;
      }
  
      /**
       * @author Mikael Grev Date: 2004-aug-02 Time: 11:31:11
       */
      public static byte[] decodeBase64(String chars, int offset, int charsLen) {
          // Check special case
          if (charsLen == 0) {
              return new byte[0];
          }
  
          int sIx = offset, eIx = offset + charsLen - 1; // Start and end index after trimming.
  
          // Trim illegal chars from start
          while (sIx < eIx && IA[chars.charAt(sIx)] < 0)
              sIx++;
  
          // Trim illegal chars from end
          while (eIx > 0 && IA[chars.charAt(eIx)] < 0)
              eIx--;
  
          // get the padding count (=) (0, 1 or 2)
          int pad = chars.charAt(eIx) == '=' ? (chars.charAt(eIx - 1) == '=' ? 2 : 1) : 0; // Count '=' at end.
          int cCnt = eIx - sIx + 1; // Content count including possible separators
          int sepCnt = charsLen > 76 ? (chars.charAt(76) == '\r' ? cCnt / 78 : 0) << 1 : 0;
  
          int len = ((cCnt - sepCnt) * 6 >> 3) - pad; // The number of decoded bytes
          byte[] bytes = new byte[len]; // Preallocate byte[] of exact length
  
          // Decode all but the last 0 - 2 bytes.
          int d = 0;
          for (int cc = 0, eLen = (len / 3) * 3; d < eLen;) {
              // Assemble three bytes into an int from four "valid" characters.
              int i = IA[chars.charAt(sIx++)] << 18
                      | IA[chars.charAt(sIx++)] << 12
                      | IA[chars.charAt(sIx++)] << 6
                      | IA[chars.charAt(sIx++)];
  
              // Add the bytes
              bytes[d++] = (byte) (i >> 16);
              bytes[d++] = (byte) (i >> 8);
              bytes[d++] = (byte) i;
  
              // If line separator, jump over it.
              if (sepCnt > 0 && ++cc == 19) {
                  sIx += 2;
                  cc = 0;
              }
          }
  
          if (d < len) {
              // Decode last 1-3 bytes (incl '=') into 1-3 bytes
              int i = 0;
              for (int j = 0; sIx <= eIx - pad; j++)
                  i |= IA[chars.charAt(sIx++)] << (18 - j * 6);
  
              for (int r = 16; d < len; r -= 8)
                  bytes[d++] = (byte) (i >> r);
          }
  
          return bytes;
      }
  
      /**
       * Decodes a BASE64 encoded string that is known to be resonably well formatted. The method is about twice as fast
       * as decode(String). The preconditions are:<br>
       * + The array must have a line length of 76 chars OR no line separators at all (one line).<br>
       * + Line separator must be "\r\n", as specified in RFC 2045 + The array must not contain illegal characters within
       * the encoded string<br>
       * + The array CAN have illegal characters at the beginning and end, those will be dealt with appropriately.<br>
       *
       * @author Mikael Grev Date: 2004-aug-02 Time: 11:31:11
       * @param s The source string. Length 0 will return an empty array. <code>null</code> will throw an exception.
       * @return The decoded array of bytes. May be of length 0.
       */
      public static byte[] decodeBase64(String s) {
          // Check special case
          int sLen = s.length();
          if (sLen == 0) {
              return new byte[0];
          }
  
          int sIx = 0, eIx = sLen - 1; // Start and end index after trimming.
  
          // Trim illegal chars from start
          while (sIx < eIx && IA[s.charAt(sIx) & 0xff] < 0)
              sIx++;
  
          // Trim illegal chars from end
          while (eIx > 0 && IA[s.charAt(eIx) & 0xff] < 0)
              eIx--;
  
          // get the padding count (=) (0, 1 or 2)
          int pad = s.charAt(eIx) == '=' ? (s.charAt(eIx - 1) == '=' ? 2 : 1) : 0; // Count '=' at end.
          int cCnt = eIx - sIx + 1; // Content count including possible separators
          int sepCnt = sLen > 76 ? (s.charAt(76) == '\r' ? cCnt / 78 : 0) << 1 : 0;
  
          int len = ((cCnt - sepCnt) * 6 >> 3) - pad; // The number of decoded bytes
          byte[] dArr = new byte[len]; // Preallocate byte[] of exact length
  
          // Decode all but the last 0 - 2 bytes.
          int d = 0;
          for (int cc = 0, eLen = (len / 3) * 3; d < eLen;) {
              // Assemble three bytes into an int from four "valid" characters.
              int i = IA[s.charAt(sIx++)] << 18 | IA[s.charAt(sIx++)] << 12 | IA[s.charAt(sIx++)] << 6
                      | IA[s.charAt(sIx++)];
  
              // Add the bytes
              dArr[d++] = (byte) (i >> 16);
              dArr[d++] = (byte) (i >> 8);
              dArr[d++] = (byte) i;
  
              // If line separator, jump over it.
              if (sepCnt > 0 && ++cc == 19) {
                  sIx += 2;
                  cc = 0;
              }
          }
  
          if (d < len) {
              // Decode last 1-3 bytes (incl '=') into 1-3 bytes
              int i = 0;
              for (int j = 0; sIx <= eIx - pad; j++)
                  i |= IA[s.charAt(sIx++)] << (18 - j * 6);
  
              for (int r = 16; d < len; r -= 8)
                  dArr[d++] = (byte) (i >> r);
          }
  
          return dArr;
      }
      
      public static int encodeUTF8(char[] chars, int offset, int len, byte[] bytes) {
          int sl = offset + len;
          int dp = 0;
          int dlASCII = dp + Math.min(len, bytes.length);
  
          // ASCII only optimized loop
          while (dp < dlASCII && chars[offset] < '\u0080') {
              bytes[dp++] = (byte) chars[offset++];
          }
  
          while (offset < sl) {
              char c = chars[offset++];
              if (c < 0x80) {
                  // Have at most seven bits
                  bytes[dp++] = (byte) c;
              } else if (c < 0x800) {
                  // 2 bytes, 11 bits
                  bytes[dp++] = (byte) (0xc0 | (c >> 6));
                  bytes[dp++] = (byte) (0x80 | (c & 0x3f));
              } else if (c >= '\uD800' && c < ('\uDFFF' + 1)) { //Character.isSurrogate(c) but 1.7
                  final int uc;
                  int ip = offset - 1;
                  if (c >= '\uD800' && c < ('\uDBFF' + 1)) { // Character.isHighSurrogate(c)
                      if (sl - ip < 2) {
                          uc = -1;
                      } else {
                          char d = chars[ip + 1];
                          // d >= '\uDC00' && d < ('\uDFFF' + 1)
                          if (d >= '\uDC00' && d < ('\uDFFF' + 1)) { // Character.isLowSurrogate(d)
                              uc = ((c << 10) + d) + (0x010000 - ('\uD800' << 10) - '\uDC00'); // Character.toCodePoint(c, d)
                          } else {
  //                            throw new JSONException("encodeUTF8 error", new MalformedInputException(1));
                              bytes[dp++] = (byte) '?';
                              continue;
                          }
                      }
                  } else {
                      //
                      if (c >= '\uDC00' && c < ('\uDFFF' + 1)) { // Character.isLowSurrogate(c)
                          bytes[dp++] = (byte) '?';
                          continue;
  //                        throw new JSONException("encodeUTF8 error", new MalformedInputException(1));
                      } else {
                          uc = c;
                      }
                  }
                  
                  if (uc < 0) {
                      bytes[dp++] = (byte) '?';
                  } else {
                      bytes[dp++] = (byte) (0xf0 | ((uc >> 18)));
                      bytes[dp++] = (byte) (0x80 | ((uc >> 12) & 0x3f));
                      bytes[dp++] = (byte) (0x80 | ((uc >> 6) & 0x3f));
                      bytes[dp++] = (byte) (0x80 | (uc & 0x3f));
                      offset++; // 2 chars
                  }
              } else {
                  // 3 bytes, 16 bits
                  bytes[dp++] = (byte) (0xe0 | ((c >> 12)));
                  bytes[dp++] = (byte) (0x80 | ((c >> 6) & 0x3f));
                  bytes[dp++] = (byte) (0x80 | (c & 0x3f));
              }
          }
          return dp;
      }
  
      /**
       * @deprecated
       */
      public static int decodeUTF8(byte[] sa, int sp, int len, char[] da) {
          final int sl = sp + len;
          int dp = 0;
          int dlASCII = Math.min(len, da.length);
  
          // ASCII only optimized loop
          while (dp < dlASCII && sa[sp] >= 0)
              da[dp++] = (char) sa[sp++];
  
          while (sp < sl) {
              int b1 = sa[sp++];
              if (b1 >= 0) {
                  // 1 byte, 7 bits: 0xxxxxxx
                  da[dp++] = (char) b1;
              } else if ((b1 >> 5) == -2 && (b1 & 0x1e) != 0) {
                  // 2 bytes, 11 bits: 110xxxxx 10xxxxxx
                  if (sp < sl) {
                      int b2 = sa[sp++];
                      if ((b2 & 0xc0) != 0x80) { // isNotContinuation(b2)
                          return -1;
                      } else {
                          da[dp++] = (char) (((b1 << 6) ^ b2)^
                                         (((byte) 0xC0 << 6) ^
                                          ((byte) 0x80 << 0)));
                      }
                      continue;
                  }
                  return -1;
              } else if ((b1 >> 4) == -2) {
                  // 3 bytes, 16 bits: 1110xxxx 10xxxxxx 10xxxxxx
                  if (sp + 1 < sl) {
                      int b2 = sa[sp++];
                      int b3 = sa[sp++];
                      if ((b1 == (byte) 0xe0 && (b2 & 0xe0) == 0x80) //
                          || (b2 & 0xc0) != 0x80 //
                          || (b3 & 0xc0) != 0x80) { // isMalformed3(b1, b2, b3)
                          return -1;
                      } else {
                          char c = (char)((b1 << 12) ^
                                            (b2 <<  6) ^
                                            (b3 ^
                                            (((byte) 0xE0 << 12) ^
                                            ((byte) 0x80 <<  6) ^
                                            ((byte) 0x80 <<  0))));
                          boolean isSurrogate = c >= '\uD800' && c < ('\uDFFF' + 1);
                          if (isSurrogate) {
                              return -1;
                          } else {
                              da[dp++] = c;
                          }
                      }
                      continue;
                  }
                  return -1;
              } else if ((b1 >> 3) == -2) {
                  // 4 bytes, 21 bits: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
                  if (sp + 2 < sl) {
                      int b2 = sa[sp++];
                      int b3 = sa[sp++];
                      int b4 = sa[sp++];
                      int uc = ((b1 << 18) ^
                                (b2 << 12) ^
                                (b3 <<  6) ^
                                (b4 ^
                                 (((byte) 0xF0 << 18) ^
                                 ((byte) 0x80 << 12) ^
                                 ((byte) 0x80 <<  6) ^
                                 ((byte) 0x80 <<  0))));
                      if (((b2 & 0xc0) != 0x80 || (b3 & 0xc0) != 0x80 || (b4 & 0xc0) != 0x80) // isMalformed4
                          ||
                          // shortest form check
                          !(uc >= 0x010000 && uc <  0X10FFFF + 1) // !Character.isSupplementaryCodePoint(uc)
                      ) {
                          return -1;
                      } else {
                          da[dp++] =  (char) ((uc >>> 10) + ('\uD800' - (0x010000 >>> 10))); // Character.highSurrogate(uc);
                          da[dp++] = (char) ((uc & 0x3ff) + '\uDC00'); // Character.lowSurrogate(uc);
                      }
                      continue;
                  }
                  return -1;
              } else {
                  return -1;
              }
          }
          return dp;
      }
  
      /**
       * @deprecated
       */
      public static String readAll(Reader reader) {
          StringBuilder buf = new StringBuilder();
  
          try {
              char[] chars = new char[2048];
              for (;;) {
                  int len = reader.read(chars, 0, chars.length);
                  if (len < 0) {
                      break;
                  }
                  buf.append(chars, 0, len);
              }
          } catch(Exception ex) {
              throw new JSONException("read string from reader error", ex);
          }
  
          return buf.toString();
      }
  
      public static boolean isValidJsonpQueryParam(String value){
          if (value == null || value.length() == 0) {
              return false;
          }
  
          for (int i = 0, len = value.length(); i < len; ++i) {
              char ch = value.charAt(i);
              if(ch != '.' && !IOUtils.isIdent(ch)){
                  return false;
              }
          }
  
          return true;
      }
  
  }
  