TypeUtils.java

package com.alibaba.fastjson2.util;

import com.alibaba.fastjson2.*;
import com.alibaba.fastjson2.reader.*;

import java.lang.invoke.*;
import java.lang.reflect.*;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.time.*;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
import java.util.function.*;

import static java.lang.invoke.MethodType.methodType;

public class TypeUtils {
    public static final Class CLASS_JSON_OBJECT_1x;
    public static final Field FIELD_JSON_OBJECT_1x_map;
    public static final Class CLASS_JSON_ARRAY_1x;

    public static final Class CLASS_SINGLE_SET = Collections.singleton(1).getClass();
    public static final Class CLASS_SINGLE_LIST = Collections.singletonList(1).getClass();
    public static final Class CLASS_UNMODIFIABLE_COLLECTION = Collections.unmodifiableCollection(new ArrayList<>()).getClass();
    public static final Class CLASS_UNMODIFIABLE_LIST = Collections.unmodifiableList(new ArrayList<>()).getClass();
    public static final Class CLASS_UNMODIFIABLE_SET = Collections.unmodifiableSet(new HashSet<>()).getClass();
    public static final Class CLASS_UNMODIFIABLE_SORTED_SET = Collections.unmodifiableSortedSet(new TreeSet<>()).getClass();
    public static final Class CLASS_UNMODIFIABLE_NAVIGABLE_SET = Collections.unmodifiableNavigableSet(new TreeSet<>()).getClass();
    public static final ParameterizedType PARAM_TYPE_LIST_STR = new ParameterizedTypeImpl(List.class, String.class);

    public static final MethodType METHOD_TYPE_SUPPLIER = MethodType.methodType(Supplier.class);
    public static final MethodType METHOD_TYPE_FUNCTION = MethodType.methodType(Function.class);
    public static final MethodType METHOD_TYPE_TO_INT_FUNCTION = MethodType.methodType(ToIntFunction.class);
    public static final MethodType METHOD_TYPE_TO_LONG_FUNCTION = MethodType.methodType(ToLongFunction.class);
    public static final MethodType METHOD_TYPE_OBJECT_INT_CONSUMER = MethodType.methodType(ObjIntConsumer.class);
    public static final MethodType METHOD_TYPE_INT_FUNCTION = MethodType.methodType(IntFunction.class);
    public static final MethodType METHOD_TYPE_LONG_FUNCTION = MethodType.methodType(LongFunction.class);
    public static final MethodType METHOD_TYPE_BI_FUNCTION = MethodType.methodType(BiFunction.class);
    public static final MethodType METHOD_TYPE_BI_CONSUMER = MethodType.methodType(BiConsumer.class);
    public static final MethodType METHOD_TYPE_VOO = MethodType.methodType(void.class, Object.class, Object.class);

    public static final MethodType METHOD_TYPE_OBJECT = MethodType.methodType(Object.class);
    public static final MethodType METHOD_TYPE_OBJECT_OBJECT = MethodType.methodType(Object.class, Object.class);
    public static final MethodType METHOD_TYPE_INT_OBJECT = MethodType.methodType(int.class, Object.class);
    public static final MethodType METHOD_TYPE_LONG_OBJECT = MethodType.methodType(long.class, Object.class);
    public static final MethodType METHOD_TYPE_VOID_OBJECT_INT = MethodType.methodType(void.class, Object.class, int.class);
    public static final MethodType METHOD_TYPE_OBJECT_LONG = MethodType.methodType(Object.class, long.class);
    public static final MethodType METHOD_TYPE_VOID_LONG = MethodType.methodType(void.class, long.class);
    public static final MethodType METHOD_TYPE_OBJECT_OBJECT_OBJECT = MethodType.methodType(Object.class, Object.class, Object.class);

    public static final MethodType METHOD_TYPE_VOID = MethodType.methodType(void.class);
    public static final MethodType METHOD_TYPE_VOID_INT = MethodType.methodType(void.class, int.class);
    public static final MethodType METHOD_TYPE_VOID_STRING = MethodType.methodType(void.class, String.class);
    public static final MethodType METHOD_TYPE_OBJECT_INT = MethodType.methodType(Object.class, int.class);

    static class X1 {
        static final Function<byte[], char[]> TO_CHARS;

        static {
            Function<byte[], char[]> toChars = null;
            if (JDKUtils.JVM_VERSION > 9) {
                try {
                    Class<?> latin1Class = Class.forName("java.lang.StringLatin1");
                    MethodHandles.Lookup lookup = JDKUtils.trustedLookup(latin1Class);
                    MethodHandle handle = lookup.findStatic(
                            latin1Class, "toChars", MethodType.methodType(char[].class, byte[].class)
                    );

                    CallSite callSite = LambdaMetafactory.metafactory(
                            lookup,
                            "apply",
                            methodType(Function.class),
                            methodType(Object.class, Object.class),
                            handle,
                            methodType(char[].class, byte[].class)
                    );
                    toChars = (Function<byte[], char[]>) callSite.getTarget().invokeExact();
                } catch (Throwable ignored) {
                    // ignored
                }
            }

            if (toChars == null) {
                toChars = TypeUtils::toAsciiCharArray;
            }
            TO_CHARS = toChars;
        }
    }

    static char[] toAsciiCharArray(byte[] bytes) {
        char[] charArray = new char[bytes.length];
        for (int i = 0; i < bytes.length; i++) {
            charArray[i] = (char) bytes[i];
        }
        return charArray;
    }

    public static Type intern(Type type) {
        if (type instanceof ParameterizedType) {
            ParameterizedType paramType = (ParameterizedType) type;
            Type rawType = paramType.getRawType();
            Type[] actualTypeArguments = paramType.getActualTypeArguments();
            if (rawType == List.class) {
                if (actualTypeArguments.length == 1) {
                    if (actualTypeArguments[0] == String.class) {
                        return PARAM_TYPE_LIST_STR;
                    }
                }
            }
        }
        return type;
    }

    public static double parseDouble(byte[] in, int off, int len) throws NumberFormatException {
        boolean isNegative = false;
        boolean signSeen = false;
        int decExp;
        byte c;
        int end = off + len;

        parseNumber:
        try {
            // throws NullPointerException if null
            if (len == 0) {
                throw new NumberFormatException("empty String");
            }
            int i = off;
            switch (in[i]) {
                case '-':
                    isNegative = true;
                case '+':
                    i++;
                    signSeen = true;
            }

            char[] digits = new char[len];

            int nDigits = 0;
            boolean decSeen = false;
            int decPt = 0;
            int nLeadZero = 0;
            int nTrailZero = 0;

            skipLeadingZerosLoop:
            while (i < end) {
                c = in[i];
                if (c == '0') {
                    nLeadZero++;
                } else if (c == '.') {
                    if (decSeen) {
                        throw new NumberFormatException("multiple points");
                    }
                    decPt = i - off;
                    if (signSeen) {
                        decPt -= 1;
                    }
                    decSeen = true;
                } else {
                    break skipLeadingZerosLoop;
                }
                i++;
            }
            digitLoop:
            while (i < end) {
                c = in[i];
                if (c >= '1' && c <= '9') {
                    digits[nDigits++] = (char) c;
                    nTrailZero = 0;
                } else if (c == '0') {
                    digits[nDigits++] = (char) c;
                    nTrailZero++;
                } else if (c == '.') {
                    if (decSeen) {
                        throw new NumberFormatException("multiple points");
                    }
                    decPt = i - off;
                    if (signSeen) {
                        decPt -= 1;
                    }
                    decSeen = true;
                } else {
                    break digitLoop;
                }
                i++;
            }
            nDigits -= nTrailZero;

            boolean isZero = (nDigits == 0);
            if (isZero && nLeadZero == 0) {
                break parseNumber; // go throw exception
            }
            if (decSeen) {
                decExp = decPt - nLeadZero;
            } else {
                decExp = nDigits + nTrailZero;
            }

            if ((i < end) && (((c = in[i]) == 'e') || (c == 'E'))) {
                int expSign = 1;
                int expVal = 0;
                int reallyBig = Integer.MAX_VALUE / 10;
                boolean expOverflow = false;
                switch (in[++i]) {
                    case '-':
                        expSign = -1;
                    case '+':
                        i++;
                }
                int expAt = i;
                expLoop:
                while (i < end) {
                    if (expVal >= reallyBig) {
                        expOverflow = true;
                    }
                    c = in[i++];
                    if (c >= '0' && c <= '9') {
                        expVal = expVal * 10 + ((int) c - (int) '0');
                    } else {
                        i--;           // back up.
                        break expLoop; // stop parsing exponent.
                    }
                }
                int expLimit = FloatingDecimal.BIG_DECIMAL_EXPONENT + nDigits + nTrailZero;
                if (expOverflow || (expVal > expLimit)) {
                    decExp = expSign * expLimit;
                } else {
                    decExp = decExp + expSign * expVal;
                }

                if (i == expAt) {
                    break parseNumber; // certainly bad
                }
            }

            if (i < end && (i != end - 1)) {
                break parseNumber; // go throw exception
            }
            if (isZero) {
                return 0;
            }
            return doubleValue(isNegative, decExp, digits, nDigits);
        } catch (StringIndexOutOfBoundsException e) {
        }
        throw new NumberFormatException("For input string: \"" + new String(in, off, len) + "\"");
    }

    public static double parseDouble(char[] in, int off, int len) throws NumberFormatException {
        boolean isNegative = false;
        boolean signSeen = false;
        int decExp;
        char c;
        int end = off + len;

        parseNumber:
        try {
            // throws NullPointerException if null
            if (len == 0) {
                throw new NumberFormatException("empty String");
            }
            int i = off;
            switch (in[i]) {
                case '-':
                    isNegative = true;
                case '+':
                    i++;
                    signSeen = true;
            }

            char[] digits = new char[len];

            int nDigits = 0;
            boolean decSeen = false;
            int decPt = 0;
            int nLeadZero = 0;
            int nTrailZero = 0;

            skipLeadingZerosLoop:
            while (i < end) {
                c = in[i];
                if (c == '0') {
                    nLeadZero++;
                } else if (c == '.') {
                    if (decSeen) {
                        throw new NumberFormatException("multiple points");
                    }
                    decPt = i - off;
                    if (signSeen) {
                        decPt -= 1;
                    }
                    decSeen = true;
                } else {
                    break skipLeadingZerosLoop;
                }
                i++;
            }
            digitLoop:
            while (i < end) {
                c = in[i];
                if (c >= '1' && c <= '9') {
                    digits[nDigits++] = c;
                    nTrailZero = 0;
                } else if (c == '0') {
                    digits[nDigits++] = c;
                    nTrailZero++;
                } else if (c == '.') {
                    if (decSeen) {
                        throw new NumberFormatException("multiple points");
                    }
                    decPt = i - off;
                    if (signSeen) {
                        decPt -= 1;
                    }
                    decSeen = true;
                } else {
                    break digitLoop;
                }
                i++;
            }
            nDigits -= nTrailZero;

            boolean isZero = (nDigits == 0);
            if (isZero && nLeadZero == 0) {
                break parseNumber; // go throw exception
            }
            if (decSeen) {
                decExp = decPt - nLeadZero;
            } else {
                decExp = nDigits + nTrailZero;
            }

            if ((i < end) && (((c = in[i]) == 'e') || (c == 'E'))) {
                int expSign = 1;
                int expVal = 0;
                int reallyBig = Integer.MAX_VALUE / 10;
                boolean expOverflow = false;
                switch (in[++i]) {
                    case '-':
                        expSign = -1;
                    case '+':
                        i++;
                }
                int expAt = i;
                expLoop:
                while (i < end) {
                    if (expVal >= reallyBig) {
                        expOverflow = true;
                    }
                    c = in[i++];
                    if (c >= '0' && c <= '9') {
                        expVal = expVal * 10 + ((int) c - (int) '0');
                    } else {
                        i--;           // back up.
                        break expLoop; // stop parsing exponent.
                    }
                }
                int expLimit = FloatingDecimal.BIG_DECIMAL_EXPONENT + nDigits + nTrailZero;
                if (expOverflow || (expVal > expLimit)) {
                    decExp = expSign * expLimit;
                } else {
                    decExp = decExp + expSign * expVal;
                }

                if (i == expAt) {
                    break parseNumber; // certainly bad
                }
            }

            if (i < end && (i != end - 1)) {
                break parseNumber; // go throw exception
            }
            if (isZero) {
                return 0;
            }
            return doubleValue(isNegative, decExp, digits, nDigits);
        } catch (StringIndexOutOfBoundsException e) {
        }
        throw new NumberFormatException("For input string: \"" + new String(in, off, len) + "\"");
    }

    public static float parseFloat(byte[] in, int off, int len) throws NumberFormatException {
        boolean isNegative = false;
        boolean signSeen = false;
        int decExp;
        byte c;
        int end = off + len;

        parseNumber:
        try {
            // throws NullPointerException if null
            if (len == 0) {
                throw new NumberFormatException("empty String");
            }
            int i = off;
            switch (in[i]) {
                case '-':
                    isNegative = true;
                case '+':
                    i++;
                    signSeen = true;
            }

            char[] digits = new char[len];

            int nDigits = 0;
            boolean decSeen = false;
            int decPt = 0;
            int nLeadZero = 0;
            int nTrailZero = 0;

            skipLeadingZerosLoop:
            while (i < end) {
                c = in[i];
                if (c == '0') {
                    nLeadZero++;
                } else if (c == '.') {
                    if (decSeen) {
                        throw new NumberFormatException("multiple points");
                    }
                    decPt = i - off;
                    if (signSeen) {
                        decPt -= 1;
                    }
                    decSeen = true;
                } else {
                    break skipLeadingZerosLoop;
                }
                i++;
            }
            digitLoop:
            while (i < end) {
                c = in[i];
                if (c >= '1' && c <= '9') {
                    digits[nDigits++] = (char) c;
                    nTrailZero = 0;
                } else if (c == '0') {
                    digits[nDigits++] = (char) c;
                    nTrailZero++;
                } else if (c == '.') {
                    if (decSeen) {
                        throw new NumberFormatException("multiple points");
                    }
                    decPt = i - off;
                    if (signSeen) {
                        decPt -= 1;
                    }
                    decSeen = true;
                } else {
                    break digitLoop;
                }
                i++;
            }
            nDigits -= nTrailZero;

            boolean isZero = (nDigits == 0);
            if (isZero && nLeadZero == 0) {
                break parseNumber; // go throw exception
            }
            if (decSeen) {
                decExp = decPt - nLeadZero;
            } else {
                decExp = nDigits + nTrailZero;
            }

            if ((i < end) && (((c = in[i]) == 'e') || (c == 'E'))) {
                int expSign = 1;
                int expVal = 0;
                int reallyBig = Integer.MAX_VALUE / 10;
                boolean expOverflow = false;
                switch (in[++i]) {
                    case '-':
                        expSign = -1;
                    case '+':
                        i++;
                }
                int expAt = i;
                expLoop:
                while (i < end) {
                    if (expVal >= reallyBig) {
                        expOverflow = true;
                    }
                    c = in[i++];
                    if (c >= '0' && c <= '9') {
                        expVal = expVal * 10 + ((int) c - (int) '0');
                    } else {
                        i--;           // back up.
                        break expLoop; // stop parsing exponent.
                    }
                }
                int expLimit = FloatingDecimal.BIG_DECIMAL_EXPONENT + nDigits + nTrailZero;
                if (expOverflow || (expVal > expLimit)) {
                    decExp = expSign * expLimit;
                } else {
                    decExp = decExp + expSign * expVal;
                }

                if (i == expAt) {
                    break parseNumber; // certainly bad
                }
            }

            if (i < end && (i != end - 1)) {
                break parseNumber; // go throw exception
            }
            if (isZero) {
                return 0;
            }
            return floatValue(isNegative, decExp, digits, nDigits);
        } catch (StringIndexOutOfBoundsException e) {
        }
        throw new NumberFormatException("For input string: \"" + new String(in, off, len) + "\"");
    }

    public static float parseFloat(char[] in, int off, int len) throws NumberFormatException {
        boolean isNegative = false;
        boolean signSeen = false;
        int decExp;
        char c;
        int end = off + len;

        parseNumber:
        try {
            // throws NullPointerException if null
            if (len == 0) {
                throw new NumberFormatException("empty String");
            }
            int i = off;
            switch (in[i]) {
                case '-':
                    isNegative = true;
                case '+':
                    i++;
                    signSeen = true;
            }

            char[] digits = new char[len];

            int nDigits = 0;
            boolean decSeen = false;
            int decPt = 0;
            int nLeadZero = 0;
            int nTrailZero = 0;

            skipLeadingZerosLoop:
            while (i < end) {
                c = in[i];
                if (c == '0') {
                    nLeadZero++;
                } else if (c == '.') {
                    if (decSeen) {
                        throw new NumberFormatException("multiple points");
                    }
                    decPt = i - off;
                    if (signSeen) {
                        decPt -= 1;
                    }
                    decSeen = true;
                } else {
                    break skipLeadingZerosLoop;
                }
                i++;
            }
            digitLoop:
            while (i < end) {
                c = in[i];
                if (c >= '1' && c <= '9') {
                    digits[nDigits++] = c;
                    nTrailZero = 0;
                } else if (c == '0') {
                    digits[nDigits++] = c;
                    nTrailZero++;
                } else if (c == '.') {
                    if (decSeen) {
                        throw new NumberFormatException("multiple points");
                    }
                    decPt = i - off;
                    if (signSeen) {
                        decPt -= 1;
                    }
                    decSeen = true;
                } else {
                    break digitLoop;
                }
                i++;
            }
            nDigits -= nTrailZero;

            boolean isZero = (nDigits == 0);
            if (isZero && nLeadZero == 0) {
                break parseNumber; // go throw exception
            }
            if (decSeen) {
                decExp = decPt - nLeadZero;
            } else {
                decExp = nDigits + nTrailZero;
            }

            if ((i < end) && (((c = in[i]) == 'e') || (c == 'E'))) {
                int expSign = 1;
                int expVal = 0;
                int reallyBig = Integer.MAX_VALUE / 10;
                boolean expOverflow = false;
                switch (in[++i]) {
                    case '-':
                        expSign = -1;
                    case '+':
                        i++;
                }
                int expAt = i;
                expLoop:
                while (i < end) {
                    if (expVal >= reallyBig) {
                        expOverflow = true;
                    }
                    c = in[i++];
                    if (c >= '0' && c <= '9') {
                        expVal = expVal * 10 + ((int) c - (int) '0');
                    } else {
                        i--;           // back up.
                        break expLoop; // stop parsing exponent.
                    }
                }
                int expLimit = FloatingDecimal.BIG_DECIMAL_EXPONENT + nDigits + nTrailZero;
                if (expOverflow || (expVal > expLimit)) {
                    decExp = expSign * expLimit;
                } else {
                    decExp = decExp + expSign * expVal;
                }

                if (i == expAt) {
                    break parseNumber; // certainly bad
                }
            }

            if (i < end && (i != end - 1)) {
                break parseNumber; // go throw exception
            }
            if (isZero) {
                return 0;
            }
            return floatValue(isNegative, decExp, digits, nDigits);
        } catch (StringIndexOutOfBoundsException e) {
        }
        throw new NumberFormatException("For input string: \"" + new String(in, off, len) + "\"");
    }

    public static double doubleValue(boolean isNegative, int decExp, char[] digits, int nDigits) {
        int kDigits = Math.min(nDigits, FloatingDecimal.MAX_DECIMAL_DIGITS + 1);

        int iValue = (int) digits[0] - (int) '0';
        int iDigits = Math.min(kDigits, FloatingDecimal.INT_DECIMAL_DIGITS);
        for (int i = 1; i < iDigits; i++) {
            iValue = iValue * 10 + (int) digits[i] - (int) '0';
        }
        long lValue = (long) iValue;
        for (int i = iDigits; i < kDigits; i++) {
            lValue = lValue * 10L + (long) ((int) digits[i] - (int) '0');
        }
        double dValue = (double) lValue;
        int exp = decExp - kDigits;

        if (nDigits <= FloatingDecimal.MAX_DECIMAL_DIGITS) {
            if (exp == 0 || dValue == 0.0) {
                return (isNegative) ? -dValue : dValue; // small floating integer
            } else if (exp >= 0) {
                if (exp <= FloatingDecimal.MAX_SMALL_TEN) {
                    double rValue = dValue * FloatingDecimal.SMALL_10_POW[exp];
                    return (isNegative) ? -rValue : rValue;
                }
                int slop = FloatingDecimal.MAX_DECIMAL_DIGITS - kDigits;
                if (exp <= FloatingDecimal.MAX_SMALL_TEN + slop) {
                    dValue *= FloatingDecimal.SMALL_10_POW[slop];
                    double rValue = dValue * FloatingDecimal.SMALL_10_POW[exp - slop];
                    return (isNegative) ? -rValue : rValue;
                }
            } else {
                if (exp >= -FloatingDecimal.MAX_SMALL_TEN) {
                    double rValue = dValue / FloatingDecimal.SMALL_10_POW[-exp];
                    return (isNegative) ? -rValue : rValue;
                }
            }
        }

        if (exp > 0) {
            if (decExp > FloatingDecimal.MAX_DECIMAL_EXPONENT + 1) {
                return (isNegative) ? Double.NEGATIVE_INFINITY : Double.POSITIVE_INFINITY;
            }
            if ((exp & 15) != 0) {
                dValue *= FloatingDecimal.SMALL_10_POW[exp & 15];
            }
            if ((exp >>= 4) != 0) {
                int j;
                for (j = 0; exp > 1; j++, exp >>= 1) {
                    if ((exp & 1) != 0) {
                        dValue *= FloatingDecimal.BIG_10_POW[j];
                    }
                }

                double t = dValue * FloatingDecimal.BIG_10_POW[j];
                if (Double.isInfinite(t)) {
                    t = dValue / 2.0;
                    t *= FloatingDecimal.BIG_10_POW[j];
                    if (Double.isInfinite(t)) {
                        return (isNegative) ? Double.NEGATIVE_INFINITY : Double.POSITIVE_INFINITY;
                    }
                    t = Double.MAX_VALUE;
                }
                dValue = t;
            }
        } else if (exp < 0) {
            exp = -exp;
            if (decExp < FloatingDecimal.MIN_DECIMAL_EXPONENT - 1) {
                return (isNegative) ? -0.0 : 0.0;
            }
            if ((exp & 15) != 0) {
                dValue /= FloatingDecimal.SMALL_10_POW[exp & 15];
            }
            if ((exp >>= 4) != 0) {
                int j;
                for (j = 0; exp > 1; j++, exp >>= 1) {
                    if ((exp & 1) != 0) {
                        dValue *= FloatingDecimal.TINY_10_POW[j];
                    }
                }

                double t = dValue * FloatingDecimal.TINY_10_POW[j];
                if (t == 0.0) {
                    t = dValue * 2.0;
                    t *= FloatingDecimal.TINY_10_POW[j];
                    if (t == 0.0) {
                        return (isNegative) ? -0.0 : 0.0;
                    }
                    t = Double.MIN_VALUE;
                }
                dValue = t;
            }
        }

        if (nDigits > FloatingDecimal.MAX_NDIGITS) {
            nDigits = FloatingDecimal.MAX_NDIGITS + 1;
            digits[FloatingDecimal.MAX_NDIGITS] = '1';
        }
        FDBigInteger bigD0 = new FDBigInteger(lValue, digits, kDigits, nDigits);
        exp = decExp - nDigits;

        long ieeeBits = Double.doubleToRawLongBits(dValue); // IEEE-754 bits of double candidate
        final int B5 = Math.max(0, -exp); // powers of 5 in bigB, value is not modified inside correctionLoop
        final int D5 = Math.max(0, exp); // powers of 5 in bigD, value is not modified inside correctionLoop
        bigD0 = bigD0.multByPow52(D5, 0);
        bigD0.makeImmutable();   // prevent bigD0 modification inside correctionLoop
        FDBigInteger bigD = null;
        int prevD2 = 0;

        correctionLoop:
        while (true) {
            // here ieeeBits can't be NaN, Infinity or zero
            int binexp = (int) (ieeeBits >>> FloatingDecimal.EXP_SHIFT);
            long bigBbits = ieeeBits & FloatingDecimal.DOUBLE_SIGNIF_BIT_MASK;
            if (binexp > 0) {
                bigBbits |= FloatingDecimal.FRACT_HOB;
            } else { // Normalize denormalized numbers.
                assert bigBbits != 0L : bigBbits; // doubleToBigInt(0.0)
                int leadingZeros = Long.numberOfLeadingZeros(bigBbits);
                int shift = leadingZeros - (63 - FloatingDecimal.EXP_SHIFT);
                bigBbits <<= shift;
                binexp = 1 - shift;
            }
            binexp -= FloatingDecimal.DOUBLE_EXP_BIAS;
            int lowOrderZeros = Long.numberOfTrailingZeros(bigBbits);
            bigBbits >>>= lowOrderZeros;
            final int bigIntExp = binexp - FloatingDecimal.EXP_SHIFT + lowOrderZeros;
            final int bigIntNBits = FloatingDecimal.EXP_SHIFT + 1 - lowOrderZeros;

            int B2 = B5; // powers of 2 in bigB
            int D2 = D5; // powers of 2 in bigD
            int Ulp2;   // powers of 2 in halfUlp.
            if (bigIntExp >= 0) {
                B2 += bigIntExp;
            } else {
                D2 -= bigIntExp;
            }
            Ulp2 = B2;
            // shift bigB and bigD left by a number s. t.
            // halfUlp is still an integer.
            int hulpbias;
            if (binexp <= -FloatingDecimal.DOUBLE_EXP_BIAS) {
                // This is going to be a denormalized number
                // (if not actually zero).
                // half an ULP is at 2^-(EXP_BIAS+EXP_SHIFT+1)
                hulpbias = binexp + lowOrderZeros + FloatingDecimal.DOUBLE_EXP_BIAS;
            } else {
                hulpbias = 1 + lowOrderZeros;
            }
            B2 += hulpbias;
            D2 += hulpbias;
            // if there are common factors of 2, we might just as well
            // factor them out, as they add nothing useful.
            int common2 = Math.min(B2, Math.min(D2, Ulp2));
            B2 -= common2;
            D2 -= common2;
            Ulp2 -= common2;
            // do multiplications by powers of 5 and 2
            FDBigInteger bigB = FDBigInteger.valueOfMulPow52(bigBbits, B5, B2);
            if (bigD == null || prevD2 != D2) {
                bigD = bigD0.leftShift(D2);
                prevD2 = D2;
            }

            FDBigInteger diff;
            int cmpResult;
            boolean overvalue;
            if ((cmpResult = bigB.cmp(bigD)) > 0) {
                overvalue = true; // our candidate is too big.
                diff = bigB.leftInplaceSub(bigD); // bigB is not user further - reuse
                if ((bigIntNBits == 1) && (bigIntExp > -FloatingDecimal.DOUBLE_EXP_BIAS + 1)) {
                    // candidate is a normalized exact power of 2 and
                    // is too big (larger than Double.MIN_NORMAL). We will be subtracting.
                    // For our purposes, ulp is the ulp of the
                    // next smaller range.
                    Ulp2 -= 1;
                    if (Ulp2 < 0) {
                        // rats. Cannot de-scale ulp this far.
                        // must scale diff in other direction.
                        Ulp2 = 0;
                        diff = diff.leftShift(1);
                    }
                }
            } else if (cmpResult < 0) {
                overvalue = false; // our candidate is too small.
                diff = bigD.rightInplaceSub(bigB); // bigB is not user further - reuse
            } else {
                // the candidate is exactly right!
                // this happens with surprising frequency
                break correctionLoop;
            }
            cmpResult = diff.cmpPow52(B5, Ulp2);
            if ((cmpResult) < 0) {
                // difference is small.
                // this is close enough
                break correctionLoop;
            } else if (cmpResult == 0) {
                // difference is exactly half an ULP
                // round to some other value maybe, then finish
                if ((ieeeBits & 1) != 0) { // half ties to even
                    ieeeBits += overvalue ? -1 : 1; // nextDown or nextUp
                }
                break correctionLoop;
            } else {
                // difference is non-trivial.
                // could scale addend by ratio of difference to
                // halfUlp here, if we bothered to compute that difference.
                // Most of the time ( I hope ) it is about 1 anyway.
                ieeeBits += overvalue ? -1 : 1; // nextDown or nextUp
                if (ieeeBits == 0 || ieeeBits == FloatingDecimal.DOUBLE_EXP_BIT_MASK) { // 0.0 or Double.POSITIVE_INFINITY
                    break correctionLoop; // oops. Fell off end of range.
                }
                continue; // try again.
            }
        }
        if (isNegative) {
            ieeeBits |= FloatingDecimal.DOUBLE_SIGN_BIT_MASK;
        }
        return Double.longBitsToDouble(ieeeBits);
    }

    public static float floatValue(boolean isNegative, int decExponent, char[] digits, int nDigits) {
        int kDigits = Math.min(nDigits, FloatingDecimal.SINGLE_MAX_DECIMAL_DIGITS + 1);
        int iValue = (int) digits[0] - (int) '0';
        for (int i = 1; i < kDigits; i++) {
            iValue = iValue * 10 + (int) digits[i] - (int) '0';
        }
        float fValue = (float) iValue;
        int exp = decExponent - kDigits;

        if (nDigits <= FloatingDecimal.SINGLE_MAX_DECIMAL_DIGITS) {
            if (exp == 0 || fValue == 0.0f) {
                return (isNegative) ? -fValue : fValue; // small floating integer
            } else if (exp >= 0) {
                if (exp <= FloatingDecimal.SINGLE_MAX_SMALL_TEN) {
                    fValue *= FloatingDecimal.SINGLE_SMALL_10_POW[exp];
                    return (isNegative) ? -fValue : fValue;
                }
                int slop = FloatingDecimal.SINGLE_MAX_DECIMAL_DIGITS - kDigits;
                if (exp <= FloatingDecimal.SINGLE_MAX_SMALL_TEN + slop) {
                    fValue *= FloatingDecimal.SINGLE_SMALL_10_POW[slop];
                    fValue *= FloatingDecimal.SINGLE_SMALL_10_POW[exp - slop];
                    return (isNegative) ? -fValue : fValue;
                }
            } else {
                if (exp >= -FloatingDecimal.SINGLE_MAX_SMALL_TEN) {
                    fValue /= FloatingDecimal.SINGLE_SMALL_10_POW[-exp];
                    return (isNegative) ? -fValue : fValue;
                }
            }
        } else if ((decExponent >= nDigits) && (nDigits + decExponent <= FloatingDecimal.MAX_DECIMAL_DIGITS)) {
            long lValue = (long) iValue;
            for (int i = kDigits; i < nDigits; i++) {
                lValue = lValue * 10L + (long) ((int) digits[i] - (int) '0');
            }
            double dValue = (double) lValue;
            exp = decExponent - nDigits;
            dValue *= FloatingDecimal.SMALL_10_POW[exp];
            fValue = (float) dValue;
            return (isNegative) ? -fValue : fValue;
        }
        double dValue = fValue;
        if (exp > 0) {
            if (decExponent > FloatingDecimal.SINGLE_MAX_DECIMAL_EXPONENT + 1) {
                return (isNegative) ? Float.NEGATIVE_INFINITY : Float.POSITIVE_INFINITY;
            }
            if ((exp & 15) != 0) {
                dValue *= FloatingDecimal.SMALL_10_POW[exp & 15];
            }
            if ((exp >>= 4) != 0) {
                int j;
                for (j = 0; exp > 0; j++, exp >>= 1) {
                    if ((exp & 1) != 0) {
                        dValue *= FloatingDecimal.BIG_10_POW[j];
                    }
                }
            }
        } else if (exp < 0) {
            exp = -exp;
            if (decExponent < FloatingDecimal.SINGLE_MIN_DECIMAL_EXPONENT - 1) {
                return (isNegative) ? -0.0f : 0.0f;
            }
            if ((exp & 15) != 0) {
                dValue /= FloatingDecimal.SMALL_10_POW[exp & 15];
            }
            if ((exp >>= 4) != 0) {
                int j;
                for (j = 0; exp > 0; j++, exp >>= 1) {
                    if ((exp & 1) != 0) {
                        dValue *= FloatingDecimal.TINY_10_POW[j];
                    }
                }
            }
        }
        fValue = Math.max(Float.MIN_VALUE, Math.min(Float.MAX_VALUE, (float) dValue));

        if (nDigits > FloatingDecimal.SINGLE_MAX_NDIGITS) {
            nDigits = FloatingDecimal.SINGLE_MAX_NDIGITS + 1;
            digits[FloatingDecimal.SINGLE_MAX_NDIGITS] = '1';
        }
        FDBigInteger bigD0 = new FDBigInteger(iValue, digits, kDigits, nDigits);
        exp = decExponent - nDigits;

        int ieeeBits = Float.floatToRawIntBits(fValue); // IEEE-754 bits of float candidate
        final int B5 = Math.max(0, -exp); // powers of 5 in bigB, value is not modified inside correctionLoop
        final int D5 = Math.max(0, exp); // powers of 5 in bigD, value is not modified inside correctionLoop
        bigD0 = bigD0.multByPow52(D5, 0);
        bigD0.makeImmutable();   // prevent bigD0 modification inside correctionLoop
        FDBigInteger bigD = null;
        int prevD2 = 0;

        correctionLoop:
        while (true) {
            // here ieeeBits can't be NaN, Infinity or zero
            int binexp = ieeeBits >>> FloatingDecimal.SINGLE_EXP_SHIFT;
            int bigBbits = ieeeBits & FloatingDecimal.FLOAT_SIGNIF_BIT_MASK;
            if (binexp > 0) {
                bigBbits |= FloatingDecimal.SINGLE_FRACT_HOB;
            } else { // Normalize denormalized numbers.
                assert bigBbits != 0 : bigBbits; // floatToBigInt(0.0)
                int leadingZeros = Integer.numberOfLeadingZeros(bigBbits);
                int shift = leadingZeros - (31 - FloatingDecimal.SINGLE_EXP_SHIFT);
                bigBbits <<= shift;
                binexp = 1 - shift;
            }
            binexp -= FloatingDecimal.FLOAT_EXP_BIAS;
            int lowOrderZeros = Integer.numberOfTrailingZeros(bigBbits);
            bigBbits >>>= lowOrderZeros;
            final int bigIntExp = binexp - FloatingDecimal.SINGLE_EXP_SHIFT + lowOrderZeros;
            final int bigIntNBits = FloatingDecimal.SINGLE_EXP_SHIFT + 1 - lowOrderZeros;

            int B2 = B5; // powers of 2 in bigB
            int D2 = D5; // powers of 2 in bigD
            int Ulp2;   // powers of 2 in halfUlp.
            if (bigIntExp >= 0) {
                B2 += bigIntExp;
            } else {
                D2 -= bigIntExp;
            }
            Ulp2 = B2;

            int hulpbias;
            if (binexp <= -FloatingDecimal.FLOAT_EXP_BIAS) {
                hulpbias = binexp + lowOrderZeros + FloatingDecimal.FLOAT_EXP_BIAS;
            } else {
                hulpbias = 1 + lowOrderZeros;
            }
            B2 += hulpbias;
            D2 += hulpbias;

            int common2 = Math.min(B2, Math.min(D2, Ulp2));
            B2 -= common2;
            D2 -= common2;
            Ulp2 -= common2;
            // do multiplications by powers of 5 and 2
            FDBigInteger bigB = FDBigInteger.valueOfMulPow52(bigBbits, B5, B2);
            if (bigD == null || prevD2 != D2) {
                bigD = bigD0.leftShift(D2);
                prevD2 = D2;
            }

            FDBigInteger diff;
            int cmpResult;
            boolean overvalue;
            if ((cmpResult = bigB.cmp(bigD)) > 0) {
                overvalue = true; // our candidate is too big.
                diff = bigB.leftInplaceSub(bigD); // bigB is not user further - reuse
                if ((bigIntNBits == 1) && (bigIntExp > -FloatingDecimal.FLOAT_EXP_BIAS + 1)) {
                    Ulp2 -= 1;
                    if (Ulp2 < 0) {
                        Ulp2 = 0;
                        diff = diff.leftShift(1);
                    }
                }
            } else if (cmpResult < 0) {
                overvalue = false; // our candidate is too small.
                diff = bigD.rightInplaceSub(bigB); // bigB is not user further - reuse
            } else {
                break correctionLoop;
            }
            cmpResult = diff.cmpPow52(B5, Ulp2);
            if ((cmpResult) < 0) {
                break correctionLoop;
            } else if (cmpResult == 0) {
                if ((ieeeBits & 1) != 0) { // half ties to even
                    ieeeBits += overvalue ? -1 : 1; // nextDown or nextUp
                }
                break correctionLoop;
            } else {
                ieeeBits += overvalue ? -1 : 1; // nextDown or nextUp
                if (ieeeBits == 0 || ieeeBits == FloatingDecimal.FLOAT_EXP_BIT_MASK) { // 0.0 or Float.POSITIVE_INFINITY
                    break correctionLoop; // oops. Fell off end of range.
                }
                continue; // try again.
            }
        }
        if (isNegative) {
            ieeeBits |= FloatingDecimal.FLOAT_SIGN_BIT_MASK;
        }
        return Float.intBitsToFloat(ieeeBits);
    }

    static class Cache {
        volatile char[] chars;
    }

    static final Cache CACHE = new Cache();
    static final AtomicReferenceFieldUpdater<Cache, char[]> CHARS_UPDATER
            = AtomicReferenceFieldUpdater.newUpdater(Cache.class, char[].class, "chars");

    public static Class<?> getMapping(Type type) {
        if (type == null) {
            return null;
        }

        if (type.getClass() == Class.class) {
            return (Class<?>) type;
        }

        if (type instanceof ParameterizedType) {
            return getMapping(((ParameterizedType) type).getRawType());
        }

        if (type instanceof TypeVariable) {
            Type boundType = ((TypeVariable<?>) type).getBounds()[0];
            if (boundType instanceof Class) {
                return (Class) boundType;
            }
            return getMapping(boundType);
        }

        if (type instanceof WildcardType) {
            Type[] upperBounds = ((WildcardType) type).getUpperBounds();
            if (upperBounds.length == 1) {
                return getMapping(upperBounds[0]);
            }
        }

        if (type instanceof GenericArrayType) {
            Type genericComponentType = ((GenericArrayType) type).getGenericComponentType();
            Class<?> componentClass = getClass(genericComponentType);
            return getArrayClass(componentClass);
        }

        return Object.class;
    }

    public static Date toDate(Object obj) {
        if (obj == null) {
            return null;
        }

        if (obj instanceof Date) {
            return (Date) obj;
        }

        if (obj instanceof Instant) {
            Instant instant = (Instant) obj;
            return new Date(
                    instant.toEpochMilli());
        }

        if (obj instanceof ZonedDateTime) {
            ZonedDateTime zdt = (ZonedDateTime) obj;
            return new Date(
                    zdt.toInstant().toEpochMilli());
        }

        if (obj instanceof LocalDate) {
            LocalDate localDate = (LocalDate) obj;
            ZonedDateTime zdt = localDate.atStartOfDay(ZoneId.systemDefault());
            return new Date(
                    zdt.toInstant().toEpochMilli());
        }

        if (obj instanceof LocalDateTime) {
            LocalDateTime ldt = (LocalDateTime) obj;
            ZonedDateTime zdt = ldt.atZone(ZoneId.systemDefault());
            return new Date(
                    zdt.toInstant().toEpochMilli());
        }

        if (obj instanceof String) {
            return DateUtils.parseDate((String) obj);
        }

        if (obj instanceof Long || obj instanceof Integer) {
            return new Date(((Number) obj).longValue());
        }

        throw new JSONException("can not cast to Date from " + obj.getClass());
    }

    public static Instant toInstant(Object obj) {
        if (obj == null) {
            return null;
        }

        if (obj instanceof Instant) {
            return (Instant) obj;
        }

        if (obj instanceof Date) {
            return ((Date) obj).toInstant();
        }

        if (obj instanceof ZonedDateTime) {
            ZonedDateTime zdt = (ZonedDateTime) obj;
            return zdt.toInstant();
        }

        if (obj instanceof String) {
            String str = (String) obj;
            if (str.isEmpty() || "null".equals(str)) {
                return null;
            }

            JSONReader jsonReader;
            if (str.charAt(0) != '"') {
                jsonReader = JSONReader.of('"' + str + '"');
            } else {
                jsonReader = JSONReader.of(str);
            }
            return jsonReader.read(Instant.class);
        }

        if (obj instanceof Map) {
            return (Instant) ObjectReaderImplInstant.INSTANCE.createInstance((Map) obj, 0L);
        }

        throw new JSONException("can not cast to Date from " + obj.getClass());
    }

    public static Object[] cast(Object obj, Type[] types) {
        if (obj == null) {
            return null;
        }

        Object[] array = new Object[types.length];

        if (obj instanceof Collection) {
            int i = 0;
            for (Object item : (Collection) obj) {
                int index = i++;
                array[index] = TypeUtils.cast(item, types[index]);
            }
        } else {
            Class<?> objectClass = obj.getClass();
            if (objectClass.isArray()) {
                int length = Array.getLength(obj);
                for (int i = 0; i < array.length && i < length; i++) {
                    Object item = Array.get(obj, i);
                    array[i] = TypeUtils.cast(item, types[i]);
                }
            } else {
                throw new JSONException("can not cast to types " + JSON.toJSONString(types) + " from " + objectClass);
            }
        }

        return array;
    }

    public static String[] toStringArray(Object object) {
        if (object == null || object instanceof String[]) {
            return (String[]) object;
        }

        if (object instanceof Collection) {
            Collection collection = (Collection) object;
            String[] array = new String[collection.size()];
            int i = 0;
            for (Object item : (Collection) object) {
                int index = i++;
                array[index] = item == null || (item instanceof String) ? (String) item : item.toString();
            }
            return array;
        }

        Class<?> objectClass = object.getClass();
        if (objectClass.isArray()) {
            int length = Array.getLength(object);
            String[] array = new String[length];
            for (int i = 0; i < array.length; i++) {
                Object item = Array.get(object, i);
                array[i] = item == null || (item instanceof String) ? (String) item : item.toString();
            }
            return array;
        }

        return cast(object, String[].class);
    }

    public static <T> T cast(Object obj, Type type) {
        if (type instanceof Class) {
            return (T) cast(obj, (Class) type);
        }

        ObjectReaderProvider provider = JSONFactory.getDefaultObjectReaderProvider();

        if (obj instanceof Collection) {
            ObjectReader objectReader = provider.getObjectReader(type);
            return (T) objectReader.createInstance((Collection) obj);
        }

        if (obj instanceof Map) {
            ObjectReader objectReader = provider.getObjectReader(type);
            return (T) objectReader.createInstance((Map) obj, 0L);
        }

        String json = JSON.toJSONString(obj);
        return JSON.parseObject(json, type);
    }

    public static <T> T cast(Object obj, Class<T> targetClass) {
        return cast(obj, targetClass, JSONFactory.getDefaultObjectReaderProvider());
    }

    public static <T> T cast(Object obj, Class<T> targetClass, ObjectReaderProvider provider) {
        if (obj == null) {
            return null;
        }

        if (targetClass.isInstance(obj)) {
            return (T) obj;
        }

        if (targetClass == Date.class) {
            return (T) toDate(obj);
        }

        if (targetClass == Instant.class) {
            return (T) toInstant(obj);
        }

        if (targetClass == String.class) {
            if (obj instanceof Character) {
                return (T) obj.toString();
            }

            return (T) JSON.toJSONString(obj);
        }

        if (targetClass == AtomicInteger.class) {
            return (T) new AtomicInteger(toIntValue(obj));
        }

        if (targetClass == AtomicLong.class) {
            return (T) new AtomicLong(toLongValue(obj));
        }

        if (targetClass == AtomicBoolean.class) {
            return (T) new AtomicBoolean((Boolean) obj);
        }

        if (obj instanceof Map) {
            ObjectReader objectReader = provider.getObjectReader(targetClass);
            return (T) objectReader.createInstance((Map) obj, 0L);
        }

        Function typeConvert = provider.getTypeConvert(obj.getClass(), targetClass);
        if (typeConvert != null) {
            return (T) typeConvert.apply(obj);
        }

        if (obj instanceof String) {
            String json = (String) obj;
            if (json.isEmpty() || "null".equals(json)) {
                return null;
            }

            JSONReader jsonReader;
            char first = json.trim().charAt(0);
            if (first == '"' || first == '{' || first == '[') {
                jsonReader = JSONReader.of(json);
            } else {
                jsonReader = JSONReader.of(
                        JSON.toJSONString(json));
            }

            ObjectReader objectReader = JSONFactory
                    .getDefaultObjectReaderProvider()
                    .getObjectReader(targetClass);
            return (T) objectReader.readObject(jsonReader, null, null, 0);
        }

        if (targetClass.isEnum()) {
            if (obj instanceof Integer) {
                int intValue = ((Integer) obj).intValue();
                ObjectReader objectReader = JSONFactory.getDefaultObjectReaderProvider().getObjectReader(targetClass);
                if (objectReader instanceof ObjectReaderImplEnum) {
                    return (T) ((ObjectReaderImplEnum) objectReader).of(intValue);
                }

                if (objectReader instanceof ObjectReaderImplEnum2X4) {
                    return (T) ((ObjectReaderImplEnum2X4) objectReader).getEnumByOrdinal(intValue);
                }
            }
        }

        if (obj instanceof Collection) {
            ObjectReader objectReader = provider.getObjectReader(targetClass);
            return (T) objectReader.createInstance((Collection) obj);
        }

        String className = targetClass.getName();
        if (obj instanceof Integer || obj instanceof Long) {
            long millis = ((Number) obj).longValue();
            switch (className) {
                case "java.sql.Date":
                    return (T) JdbcSupport.createDate(millis);
                case "java.sql.Timestamp":
                    return (T) JdbcSupport.createTimestamp(millis);
                case "java.sql.Time":
                    return (T) JdbcSupport.createTime(millis);
                default:
                    break;
            }
        }

        throw new JSONException("can not cast to " + className + ", from " + obj.getClass());
    }

    static final Map<Class, String> NAME_MAPPINGS = new IdentityHashMap<>();
    static final Map<String, Class> TYPE_MAPPINGS = new ConcurrentHashMap<>();

    static {
        CLASS_JSON_OBJECT_1x = loadClass("com.alibaba.fastjson.JSONObject");
        {
            Field field = null;
            if (CLASS_JSON_OBJECT_1x != null) {
                try {
                    field = CLASS_JSON_OBJECT_1x.getDeclaredField("map");
                    field.setAccessible(true);
                } catch (Throwable ignore) {
                    // ignore
                }
            }
            FIELD_JSON_OBJECT_1x_map = field;
        }

        CLASS_JSON_ARRAY_1x = loadClass("com.alibaba.fastjson.JSONArray");

        NAME_MAPPINGS.put(byte.class, "B");
        NAME_MAPPINGS.put(short.class, "S");
        NAME_MAPPINGS.put(int.class, "I");
        NAME_MAPPINGS.put(long.class, "J");
        NAME_MAPPINGS.put(float.class, "F");
        NAME_MAPPINGS.put(double.class, "D");
        NAME_MAPPINGS.put(char.class, "C");
        NAME_MAPPINGS.put(boolean.class, "Z");

        NAME_MAPPINGS.put(Object[].class, "[O");
        NAME_MAPPINGS.put(Object[][].class, "[[O");
        NAME_MAPPINGS.put(byte[].class, "[B");
        NAME_MAPPINGS.put(byte[][].class, "[[B");
        NAME_MAPPINGS.put(short[].class, "[S");
        NAME_MAPPINGS.put(short[][].class, "[[S");
        NAME_MAPPINGS.put(int[].class, "[I");
        NAME_MAPPINGS.put(int[][].class, "[[I");
        NAME_MAPPINGS.put(long[].class, "[J");
        NAME_MAPPINGS.put(long[][].class, "[[J");
        NAME_MAPPINGS.put(float[].class, "[F");
        NAME_MAPPINGS.put(float[][].class, "[[F");
        NAME_MAPPINGS.put(double[].class, "[D");
        NAME_MAPPINGS.put(double[][].class, "[[D");
        NAME_MAPPINGS.put(char[].class, "[C");
        NAME_MAPPINGS.put(char[][].class, "[[C");
        NAME_MAPPINGS.put(boolean[].class, "[Z");
        NAME_MAPPINGS.put(boolean[][].class, "[[Z");

        NAME_MAPPINGS.put(Byte[].class, "[Byte");
        NAME_MAPPINGS.put(Byte[][].class, "[[Byte");
        NAME_MAPPINGS.put(Short[].class, "[Short");
        NAME_MAPPINGS.put(Short[][].class, "[[Short");
        NAME_MAPPINGS.put(Integer[].class, "[Integer");
        NAME_MAPPINGS.put(Integer[][].class, "[[Integer");
        NAME_MAPPINGS.put(Long[].class, "[Long");
        NAME_MAPPINGS.put(Long[][].class, "[[Long");
        NAME_MAPPINGS.put(Float[].class, "[Float");
        NAME_MAPPINGS.put(Float[][].class, "[[Float");
        NAME_MAPPINGS.put(Double[].class, "[Double");
        NAME_MAPPINGS.put(Double[][].class, "[[Double");
        NAME_MAPPINGS.put(Character[].class, "[Character");
        NAME_MAPPINGS.put(Character[][].class, "[[Character");
        NAME_MAPPINGS.put(Boolean[].class, "[Boolean");
        NAME_MAPPINGS.put(Boolean[][].class, "[[Boolean");

        NAME_MAPPINGS.put(String[].class, "[String");
        NAME_MAPPINGS.put(String[][].class, "[[String");

        NAME_MAPPINGS.put(BigDecimal[].class, "[BigDecimal");
        NAME_MAPPINGS.put(BigDecimal[][].class, "[[BigDecimal");

        NAME_MAPPINGS.put(BigInteger[].class, "[BigInteger");
        NAME_MAPPINGS.put(BigInteger[][].class, "[[BigInteger");

        NAME_MAPPINGS.put(UUID[].class, "[UUID");
        NAME_MAPPINGS.put(UUID[][].class, "[[UUID");

        NAME_MAPPINGS.put(Object.class, "Object");
        NAME_MAPPINGS.put(Object[].class, "[O");

        NAME_MAPPINGS.put(HashMap.class, "M");
        TYPE_MAPPINGS.put("HashMap", HashMap.class);
        TYPE_MAPPINGS.put("java.util.HashMap", HashMap.class);

        NAME_MAPPINGS.put(LinkedHashMap.class, "LM");
        TYPE_MAPPINGS.put("LinkedHashMap", LinkedHashMap.class);
        TYPE_MAPPINGS.put("java.util.LinkedHashMap", LinkedHashMap.class);

        NAME_MAPPINGS.put(TreeMap.class, "TM");
        TYPE_MAPPINGS.put("TreeMap", TreeMap.class);

        NAME_MAPPINGS.put(ArrayList.class, "A");
        TYPE_MAPPINGS.put("ArrayList", ArrayList.class);
        TYPE_MAPPINGS.put("java.util.ArrayList", ArrayList.class);

        NAME_MAPPINGS.put(LinkedList.class, "LA");
        TYPE_MAPPINGS.put("LA", LinkedList.class);
        TYPE_MAPPINGS.put("LinkedList", LinkedList.class);
        TYPE_MAPPINGS.put("java.util.LinkedList", LinkedList.class);
        TYPE_MAPPINGS.put("java.util.concurrent.ConcurrentLinkedQueue", ConcurrentLinkedQueue.class);
        TYPE_MAPPINGS.put("java.util.concurrent.ConcurrentLinkedDeque", ConcurrentLinkedDeque.class);

        //java.util.LinkedHashMap.class,

        NAME_MAPPINGS.put(HashSet.class, "HashSet");
        NAME_MAPPINGS.put(TreeSet.class, "TreeSet");
        NAME_MAPPINGS.put(LinkedHashSet.class, "LinkedHashSet");
        NAME_MAPPINGS.put(ConcurrentHashMap.class, "ConcurrentHashMap");
        NAME_MAPPINGS.put(ConcurrentLinkedQueue.class, "ConcurrentLinkedQueue");
        NAME_MAPPINGS.put(ConcurrentLinkedDeque.class, "ConcurrentLinkedDeque");
        NAME_MAPPINGS.put(JSONObject.class, "JSONObject");
        NAME_MAPPINGS.put(JSONArray.class, "JSONArray");
        NAME_MAPPINGS.put(Currency.class, "Currency");
        NAME_MAPPINGS.put(TimeUnit.class, "TimeUnit");

        Class<?>[] classes = new Class[]{
                Object.class,
                Cloneable.class,
                AutoCloseable.class,
                java.lang.Exception.class,
                java.lang.RuntimeException.class,
                java.lang.IllegalAccessError.class,
                java.lang.IllegalAccessException.class,
                java.lang.IllegalArgumentException.class,
                java.lang.IllegalMonitorStateException.class,
                java.lang.IllegalStateException.class,
                java.lang.IllegalThreadStateException.class,
                java.lang.IndexOutOfBoundsException.class,
                java.lang.InstantiationError.class,
                java.lang.InstantiationException.class,
                java.lang.InternalError.class,
                java.lang.InterruptedException.class,
                java.lang.LinkageError.class,
                java.lang.NegativeArraySizeException.class,
                java.lang.NoClassDefFoundError.class,
                java.lang.NoSuchFieldError.class,
                java.lang.NoSuchFieldException.class,
                java.lang.NoSuchMethodError.class,
                java.lang.NoSuchMethodException.class,
                java.lang.NullPointerException.class,
                java.lang.NumberFormatException.class,
                java.lang.OutOfMemoryError.class,
                java.lang.SecurityException.class,
                java.lang.StackOverflowError.class,
                java.lang.StringIndexOutOfBoundsException.class,
                java.lang.TypeNotPresentException.class,
                java.lang.VerifyError.class,
                java.lang.StackTraceElement.class,
                java.util.Hashtable.class,
                java.util.TreeMap.class,
                java.util.IdentityHashMap.class,
                java.util.WeakHashMap.class,
                java.util.HashSet.class,
                java.util.LinkedHashSet.class,
                java.util.TreeSet.class,
                java.util.LinkedList.class,
                java.util.concurrent.TimeUnit.class,
                java.util.concurrent.ConcurrentHashMap.class,
                java.util.concurrent.atomic.AtomicInteger.class,
                java.util.concurrent.atomic.AtomicLong.class,
                java.util.Collections.EMPTY_MAP.getClass(),
                java.lang.Boolean.class,
                java.lang.Character.class,
                java.lang.Byte.class,
                java.lang.Short.class,
                java.lang.Integer.class,
                java.lang.Long.class,
                java.lang.Float.class,
                java.lang.Double.class,
                java.lang.Number.class,
                java.lang.String.class,
                java.math.BigDecimal.class,
                java.math.BigInteger.class,
                java.util.BitSet.class,
                java.util.Calendar.class,
                java.util.Date.class,
                java.util.Locale.class,
                java.util.UUID.class,
                java.util.Currency.class,
                java.text.SimpleDateFormat.class,
                JSONObject.class,
                JSONArray.class,
                java.util.concurrent.ConcurrentSkipListMap.class,
                java.util.concurrent.ConcurrentSkipListSet.class,
        };
        for (Class clazz : classes) {
            TYPE_MAPPINGS.put(clazz.getSimpleName(), clazz);
            TYPE_MAPPINGS.put(clazz.getName(), clazz);
            NAME_MAPPINGS.put(clazz, clazz.getSimpleName());
        }

        TYPE_MAPPINGS.put("JO10", JSONObject1O.class);
        TYPE_MAPPINGS.put("[O", Object[].class);
        TYPE_MAPPINGS.put("[Ljava.lang.Object;", Object[].class);
        TYPE_MAPPINGS.put("[java.lang.Object", Object[].class);
        TYPE_MAPPINGS.put("[Object", Object[].class);
        TYPE_MAPPINGS.put("StackTraceElement", StackTraceElement.class);
        TYPE_MAPPINGS.put("[StackTraceElement", StackTraceElement[].class);

        String[] items = new String[]{
                "java.util.Collections$UnmodifiableMap",
                "java.util.Collections$UnmodifiableCollection",
        };

        for (String className : items) {
            Class<?> clazz = loadClass(className);
            TYPE_MAPPINGS.put(clazz.getName(), clazz);
        }

        {
            if (CLASS_JSON_OBJECT_1x != null) {
                TYPE_MAPPINGS.putIfAbsent("JO1", CLASS_JSON_OBJECT_1x);
                TYPE_MAPPINGS.putIfAbsent(CLASS_JSON_OBJECT_1x.getName(), CLASS_JSON_OBJECT_1x);
            }
            if (CLASS_JSON_ARRAY_1x != null) {
                TYPE_MAPPINGS.putIfAbsent("JA1", CLASS_JSON_ARRAY_1x);
                TYPE_MAPPINGS.putIfAbsent(CLASS_JSON_ARRAY_1x.getName(), CLASS_JSON_ARRAY_1x);
            }
        }

        NAME_MAPPINGS.put(new HashMap().keySet().getClass(), "Set");
        NAME_MAPPINGS.put(new LinkedHashMap().keySet().getClass(), "Set");
        NAME_MAPPINGS.put(new TreeMap<>().keySet().getClass(), "Set");
        NAME_MAPPINGS.put(((Map) new ConcurrentHashMap()).keySet().getClass(), "Set"); // bug fix for android9
        NAME_MAPPINGS.put(((Map) new ConcurrentSkipListMap()).keySet().getClass(), "Set"); // bug fix for android9
        TYPE_MAPPINGS.put("Set", HashSet.class);

        NAME_MAPPINGS.put(new HashMap().values().getClass(), "List");
        NAME_MAPPINGS.put(new LinkedHashMap().values().getClass(), "List");
        NAME_MAPPINGS.put(new TreeMap().values().getClass(), "List");
        NAME_MAPPINGS.put(new ConcurrentHashMap().values().getClass(), "List");
        NAME_MAPPINGS.put(new ConcurrentSkipListMap().values().getClass(), "List");

        TYPE_MAPPINGS.put("List", ArrayList.class);
        TYPE_MAPPINGS.put("java.util.ImmutableCollections$Map1", HashMap.class);
        TYPE_MAPPINGS.put("java.util.ImmutableCollections$MapN", LinkedHashMap.class);
        TYPE_MAPPINGS.put("java.util.ImmutableCollections$Set12", LinkedHashSet.class);
        TYPE_MAPPINGS.put("java.util.ImmutableCollections$SetN", LinkedHashSet.class);
        TYPE_MAPPINGS.put("java.util.ImmutableCollections$List12", ArrayList.class);
        TYPE_MAPPINGS.put("java.util.ImmutableCollections$ListN", ArrayList.class);
        TYPE_MAPPINGS.put("java.util.ImmutableCollections$SubList", ArrayList.class);

        for (Map.Entry<Class, String> entry : NAME_MAPPINGS.entrySet()) {
            TYPE_MAPPINGS.putIfAbsent(entry.getValue(), entry.getKey());
        }
    }

    public static String getTypeName(Class type) {
        String mapTypeName = NAME_MAPPINGS.get(type);
        if (mapTypeName != null) {
            return mapTypeName;
        }

        if (Proxy.isProxyClass(type)) {
            Class[] interfaces = type.getInterfaces();
            if (interfaces.length > 0) {
                type = interfaces[0];
            }
        }

        String typeName = type.getTypeName();
        switch (typeName) {
            case "com.alibaba.fastjson.JSONObject":
                NAME_MAPPINGS.putIfAbsent(type, "JO1");
                return NAME_MAPPINGS.get(type);
            case "com.alibaba.fastjson.JSONArray":
                NAME_MAPPINGS.putIfAbsent(type, "JA1");
                return NAME_MAPPINGS.get(type);
//            case "org.apache.commons.lang3.tuple.ImmutablePair":
//                NAME_MAPPINGS.putIfAbsent(type, "org.apache.commons.lang3.tuple.Pair");
//                return NAME_MAPPINGS.get(type);
            default:
                break;
        }

        return typeName;
    }

    public static Class getMapping(String typeName) {
        return TYPE_MAPPINGS.get(typeName);
    }

    public static BigDecimal toBigDecimal(Object value) {
        if (value == null || value instanceof BigDecimal) {
            return (BigDecimal) value;
        }

        if (value instanceof Byte || value instanceof Short || value instanceof Integer || value instanceof Long) {
            return BigDecimal.valueOf(((Number) value).longValue());
        }

        if (value instanceof String) {
            String str = (String) value;
            if (str.isEmpty() || "null".equals(str)) {
                return null;
            }
            return new BigDecimal(str);
        }

        throw new JSONException("can not cast to decimal from " + value.getClass());
    }

    public static BigDecimal toBigDecimal(long i) {
        return BigDecimal.valueOf(i);
    }

    public static BigDecimal toBigDecimal(float f) {
        return toBigDecimal(Float.toString(f));
    }

    public static BigDecimal toBigDecimal(double d) {
        return toBigDecimal(Double.toString(d));
    }

    public static BigDecimal toBigDecimal(String str) {
        if (str == null || str.isEmpty() || "null".equals(str)) {
            return null;
        }

        if (JDKUtils.STRING_CODER != null) {
            int code = JDKUtils.STRING_CODER.applyAsInt(str);
            if (code == JDKUtils.LATIN1 && JDKUtils.STRING_VALUE != null) {
                byte[] bytes = JDKUtils.STRING_VALUE.apply(str);
                return toBigDecimal(bytes);
            }
        }

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

    public static BigDecimal toBigDecimal(char[] chars) {
        if (chars == null || chars.length == 0) {
            return null;
        }

        boolean negative = false;
        int j = 0;
        if (chars[0] == '-') {
            negative = true;
            j = 1;
        }

        if (chars.length <= 20 || (negative && chars.length == 21)) {
            int dot = 0;
            int dotIndex = -1;
            long unscaleValue = 0;
            for (; j < chars.length; j++) {
                char b = chars[j];
                if (b == '.') {
                    dot++;
                    if (dot > 1) {
                        break;
                    }
                    dotIndex = j;
                } else if (b >= '0' && b <= '9') {
                    unscaleValue = unscaleValue * 10 + (b - '0');
                } else {
                    unscaleValue = -1;
                    break;
                }
            }
            int scale = 0;
            if (unscaleValue >= 0 && dot <= 1) {
                if (negative) {
                    unscaleValue = -unscaleValue;
                }
                if (dotIndex != -1) {
                    scale = chars.length - dotIndex - 1;
                }
                return BigDecimal.valueOf(unscaleValue, scale);
            }
        }

        return new BigDecimal(chars, 0, chars.length);
    }

    public static BigDecimal toBigDecimal(byte[] strBytes) {
        if (strBytes == null || strBytes.length == 0) {
            return null;
        }

        boolean negative = false;
        int j = 0;
        if (strBytes[0] == '-') {
            negative = true;
            j = 1;
        }

        if (strBytes.length <= 20 || (negative && strBytes.length == 21)) {
            int dot = 0;
            int dotIndex = -1;
            long unscaleValue = 0;
            for (; j < strBytes.length; j++) {
                byte b = strBytes[j];
                if (b == '.') {
                    dot++;
                    if (dot > 1) {
                        break;
                    }
                    dotIndex = j;
                } else if (b >= '0' && b <= '9') {
                    unscaleValue = unscaleValue * 10 + (b - '0');
                } else {
                    unscaleValue = -1;
                    break;
                }
            }
            int scale = 0;
            if (unscaleValue >= 0 && dot <= 1) {
                if (negative) {
                    unscaleValue = -unscaleValue;
                }
                if (dotIndex != -1) {
                    scale = strBytes.length - dotIndex - 1;
                }
                return BigDecimal.valueOf(unscaleValue, scale);
            }
        }

        char[] chars = X1.TO_CHARS.apply(strBytes);
        return new BigDecimal(chars, 0, chars.length);
    }

    public static BigInteger toBigInteger(Object value) {
        if (value == null || value instanceof BigInteger) {
            return (BigInteger) value;
        }

        if (value instanceof Byte || value instanceof Short || value instanceof Integer || value instanceof Long) {
            return BigInteger.valueOf(((Number) value).longValue());
        }

        if (value instanceof String) {
            String str = (String) value;
            if (str.isEmpty() || "null".equals(str)) {
                return null;
            }
            return new BigInteger(str);
        }

        throw new JSONException("can not cast to bigint");
    }

    public static Long toLong(Object value) {
        if (value == null || value instanceof Long) {
            return (Long) value;
        }

        if (value instanceof Number) {
            return Long.valueOf(((Number) value).longValue());
        }

        if (value instanceof String) {
            String str = (String) value;
            if (str.isEmpty() || "null".equals(str)) {
                return null;
            }
            return Long.parseLong(str);
        }

        throw new JSONException("can not cast to long, class " + value.getClass());
    }

    public static long toLongValue(Object value) {
        if (value == null) {
            return 0L;
        }

        if (value instanceof Long) {
            return (Long) value;
        }

        if (value instanceof Number) {
            return ((Number) value).longValue();
        }

        if (value instanceof String) {
            String str = (String) value;
            if (str.isEmpty() || "null".equals(str)) {
                return 0;
            }
            return Long.parseLong(str);
        }

        throw new JSONException("can not cast to long from " + value.getClass());
    }

    public static Boolean parseBoolean(byte[] bytes, int off, int len) {
        switch (len) {
            case 0:
                return null;
            case 1: {
                byte b0 = bytes[off];
                if (b0 == '1' || b0 == 'Y') {
                    return Boolean.TRUE;
                }
                if (b0 == '0' || b0 == 'N') {
                    return Boolean.FALSE;
                }
                break;
            }
            case 4:
                if (bytes[off] == 't' && bytes[off + 1] == 'r' && bytes[off + 2] == 'u' && bytes[off + 3] == 'e') {
                    return Boolean.TRUE;
                }
                break;
            case 5:
                if (bytes[off] == 'f'
                        && bytes[off + 1] == 'a'
                        && bytes[off + 2] == 'l'
                        && bytes[off + 3] == 's'
                        && bytes[off + 4] == 'e') {
                    return Boolean.FALSE;
                }
                break;
            default:
                break;
        }

        String str = new String(bytes, off, len);
        return Boolean.parseBoolean(str);
    }

    public static Integer parseInt(byte[] bytes, int off, int len) {
        switch (len) {
            case 0:
                return null;
            case 1: {
                byte b0 = bytes[off];
                if (b0 >= '0' && b0 <= '9') {
                    return b0 - '0';
                }
                break;
            }
            case 2: {
                byte b0 = bytes[off];
                byte b1 = bytes[off + 1];
                if (b0 >= '0' && b0 <= '9'
                        && b1 >= '0' && b1 <= '9') {
                    return (b0 - '0') * 10
                            + (b1 - '0');
                }
                break;
            }
            case 3: {
                byte b0 = bytes[off];
                byte b1 = bytes[off + 1];
                byte b2 = bytes[off + 2];
                if (b0 >= '0' && b0 <= '9'
                        && b1 >= '0' && b1 <= '9'
                        && b2 >= '0' && b2 <= '9') {
                    return (b0 - '0') * 100
                            + (b1 - '0') * 10
                            + (b2 - '0');
                }
                break;
            }
            case 4: {
                byte b0 = bytes[off];
                byte b1 = bytes[off + 1];
                byte b2 = bytes[off + 2];
                byte b3 = bytes[off + 3];
                if (b0 >= '0' && b0 <= '9'
                        && b1 >= '0' && b1 <= '9'
                        && b2 >= '0' && b2 <= '9'
                        && b3 >= '0' && b3 <= '9'
                ) {
                    return (b0 - '0') * 1000
                            + (b1 - '0') * 100
                            + (b2 - '0') * 10
                            + (b3 - '0');
                }
                break;
            }
            case 5: {
                byte b0 = bytes[off];
                byte b1 = bytes[off + 1];
                byte b2 = bytes[off + 2];
                byte b3 = bytes[off + 3];
                byte b4 = bytes[off + 4];
                if (b0 >= '0' && b0 <= '9'
                        && b1 >= '0' && b1 <= '9'
                        && b2 >= '0' && b2 <= '9'
                        && b3 >= '0' && b3 <= '9'
                        && b4 >= '0' && b4 <= '9'
                ) {
                    return (b0 - '0') * 10_000
                            + (b1 - '0') * 1000
                            + (b2 - '0') * 100
                            + (b3 - '0') * 10
                            + (b4 - '0');
                }
                break;
            }
            case 6: {
                byte b0 = bytes[off];
                byte b1 = bytes[off + 1];
                byte b2 = bytes[off + 2];
                byte b3 = bytes[off + 3];
                byte b4 = bytes[off + 4];
                byte b5 = bytes[off + 5];
                if (b0 >= '0' && b0 <= '9'
                        && b1 >= '0' && b1 <= '9'
                        && b2 >= '0' && b2 <= '9'
                        && b3 >= '0' && b3 <= '9'
                        && b4 >= '0' && b4 <= '9'
                        && b5 >= '0' && b5 <= '9'
                ) {
                    return (b0 - '0') * 100_000
                            + (b1 - '0') * 10_000
                            + (b2 - '0') * 1_000
                            + (b3 - '0') * 100
                            + (b4 - '0') * 10
                            + (b5 - '0');
                }
                break;
            }
            case 7: {
                byte b0 = bytes[off];
                byte b1 = bytes[off + 1];
                byte b2 = bytes[off + 2];
                byte b3 = bytes[off + 3];
                byte b4 = bytes[off + 4];
                byte b5 = bytes[off + 5];
                byte b6 = bytes[off + 6];
                if (b0 >= '0' && b0 <= '9'
                        && b1 >= '0' && b1 <= '9'
                        && b2 >= '0' && b2 <= '9'
                        && b3 >= '0' && b3 <= '9'
                        && b4 >= '0' && b4 <= '9'
                        && b5 >= '0' && b5 <= '9'
                        && b6 >= '0' && b6 <= '9'
                ) {
                    return (b0 - '0') * 1_000_000
                            + (b1 - '0') * 100_000
                            + (b2 - '0') * 10_000
                            + (b3 - '0') * 1_000
                            + (b4 - '0') * 100
                            + (b5 - '0') * 10
                            + (b6 - '0');
                }
                break;
            }
            case 8: {
                byte b0 = bytes[off];
                byte b1 = bytes[off + 1];
                byte b2 = bytes[off + 2];
                byte b3 = bytes[off + 3];
                byte b4 = bytes[off + 4];
                byte b5 = bytes[off + 5];
                byte b6 = bytes[off + 6];
                byte b7 = bytes[off + 7];
                if (b0 >= '0' && b0 <= '9'
                        && b1 >= '0' && b1 <= '9'
                        && b2 >= '0' && b2 <= '9'
                        && b3 >= '0' && b3 <= '9'
                        && b4 >= '0' && b4 <= '9'
                        && b5 >= '0' && b5 <= '9'
                        && b6 >= '0' && b6 <= '9'
                        && b7 >= '0' && b7 <= '9'
                ) {
                    return (b0 - '0') * 10_000_000
                            + (b1 - '0') * 1_000_000
                            + (b2 - '0') * 100_000
                            + (b3 - '0') * 10_000
                            + (b4 - '0') * 1_000
                            + (b5 - '0') * 100
                            + (b6 - '0') * 10
                            + (b7 - '0');
                }
                break;
            }
            default:
                break;
        }

        String str = new String(bytes, off, len);
        return Integer.parseInt(str);
    }

    public static Long parseLong(byte[] bytes, int off, int len) {
        switch (len) {
            case 0:
                return null;
            case 1: {
                byte b0 = bytes[off];
                if (b0 >= '0' && b0 <= '9') {
                    return (long) (b0 - '0');
                }
                break;
            }
            case 2: {
                byte b0 = bytes[off];
                byte b1 = bytes[off + 1];
                if (b0 >= '0' && b0 <= '9'
                        && b1 >= '0' && b1 <= '9') {
                    return (long) (b0 - '0') * 10
                            + (b1 - '0');
                }
                break;
            }
            case 3: {
                byte b0 = bytes[off];
                byte b1 = bytes[off + 1];
                byte b2 = bytes[off + 2];
                if (b0 >= '0' && b0 <= '9'
                        && b1 >= '0' && b1 <= '9'
                        && b2 >= '0' && b2 <= '9') {
                    return (long) (b0 - '0') * 100
                            + (b1 - '0') * 10
                            + (b2 - '0');
                }
                break;
            }
            case 4: {
                byte b0 = bytes[off];
                byte b1 = bytes[off + 1];
                byte b2 = bytes[off + 2];
                byte b3 = bytes[off + 3];
                if (b0 >= '0' && b0 <= '9'
                        && b1 >= '0' && b1 <= '9'
                        && b2 >= '0' && b2 <= '9'
                        && b3 >= '0' && b3 <= '9') {
                    return (long) (b0 - '0') * 1000
                            + (b1 - '0') * 100
                            + (b2 - '0') * 10
                            + (b3 - '0');
                }
                break;
            }
            case 5: {
                byte b0 = bytes[off];
                byte b1 = bytes[off + 1];
                byte b2 = bytes[off + 2];
                byte b3 = bytes[off + 3];
                byte b4 = bytes[off + 4];
                if (b0 >= '0' && b0 <= '9'
                        && b1 >= '0' && b1 <= '9'
                        && b2 >= '0' && b2 <= '9'
                        && b3 >= '0' && b3 <= '9'
                        && b4 >= '0' && b4 <= '9'
                ) {
                    return (long) (b0 - '0') * 10_000
                            + (b1 - '0') * 1000
                            + (b2 - '0') * 100
                            + (b3 - '0') * 10
                            + (b4 - '0');
                }
                break;
            }
            case 6: {
                byte b0 = bytes[off];
                byte b1 = bytes[off + 1];
                byte b2 = bytes[off + 2];
                byte b3 = bytes[off + 3];
                byte b4 = bytes[off + 4];
                byte b5 = bytes[off + 5];
                if (b0 >= '0' && b0 <= '9'
                        && b1 >= '0' && b1 <= '9'
                        && b2 >= '0' && b2 <= '9'
                        && b3 >= '0' && b3 <= '9'
                        && b4 >= '0' && b4 <= '9'
                        && b5 >= '0' && b5 <= '9'
                ) {
                    return (long) (b0 - '0') * 100_000
                            + (b1 - '0') * 10_000
                            + (b2 - '0') * 1_000
                            + (b3 - '0') * 100
                            + (b4 - '0') * 10
                            + (b5 - '0');
                }
                break;
            }
            case 7: {
                byte b0 = bytes[off];
                byte b1 = bytes[off + 1];
                byte b2 = bytes[off + 2];
                byte b3 = bytes[off + 3];
                byte b4 = bytes[off + 4];
                byte b5 = bytes[off + 5];
                byte b6 = bytes[off + 6];
                if (b0 >= '0' && b0 <= '9'
                        && b1 >= '0' && b1 <= '9'
                        && b2 >= '0' && b2 <= '9'
                        && b3 >= '0' && b3 <= '9'
                        && b4 >= '0' && b4 <= '9'
                        && b5 >= '0' && b5 <= '9'
                        && b6 >= '0' && b6 <= '9'
                ) {
                    return (long) (b0 - '0') * 1_000_000
                            + (b1 - '0') * 100_000
                            + (b2 - '0') * 10_000
                            + (b3 - '0') * 1_000
                            + (b4 - '0') * 100
                            + (b5 - '0') * 10
                            + (b6 - '0');
                }
                break;
            }
            case 8: {
                byte b0 = bytes[off];
                byte b1 = bytes[off + 1];
                byte b2 = bytes[off + 2];
                byte b3 = bytes[off + 3];
                byte b4 = bytes[off + 4];
                byte b5 = bytes[off + 5];
                byte b6 = bytes[off + 6];
                byte b7 = bytes[off + 7];
                if (b0 >= '0' && b0 <= '9'
                        && b1 >= '0' && b1 <= '9'
                        && b2 >= '0' && b2 <= '9'
                        && b3 >= '0' && b3 <= '9'
                        && b4 >= '0' && b4 <= '9'
                        && b5 >= '0' && b5 <= '9'
                        && b6 >= '0' && b6 <= '9'
                        && b7 >= '0' && b7 <= '9'
                ) {
                    return (long) (b0 - '0') * 10_000_000
                            + (b1 - '0') * 1_000_000
                            + (b2 - '0') * 100_000
                            + (b3 - '0') * 10_000
                            + (b4 - '0') * 1_000
                            + (b5 - '0') * 100
                            + (b6 - '0') * 10
                            + (b7 - '0');
                }
                break;
            }
            default:
                break;
        }

        String str = new String(bytes, off, len);
        return Long.parseLong(str);
    }

    public static BigDecimal parseBigDecimal(byte[] bytes, int off, int len) {
        if (len == 0) {
            return null;
        }

        char[] chars = new char[len];
        for (int i = 0; i < len; i++) {
            chars[i] = (char) bytes[off + i];
        }
        return new BigDecimal(chars, 0, chars.length);
    }

    public static Integer toInteger(Object value) {
        if (value == null || value instanceof Integer) {
            return (Integer) value;
        }

        if (value instanceof Number) {
            return Integer.valueOf(((Number) value).intValue());
        }

        if (value instanceof String) {
            String str = (String) value;
            if (str.isEmpty() || "null".equals(str)) {
                return null;
            }
            return Integer.parseInt(str);
        }

        if (value instanceof Map && ((Map<?, ?>) value).isEmpty()) {
            return null;
        }

        if (value instanceof Boolean) {
            return ((Boolean) value).booleanValue() ? 1 : 0;
        }

        throw new JSONException("can not cast to integer");
    }

    public static Byte toByte(Object value) {
        if (value == null || value instanceof Byte) {
            return (Byte) value;
        }

        if (value instanceof Number) {
            return ((Number) value).byteValue();
        }

        if (value instanceof String) {
            String str = (String) value;
            if (str.isEmpty() || "null".equals(str)) {
                return null;
            }
            return Byte.parseByte(str);
        }

        throw new JSONException("can not cast to byte");
    }

    public static byte toByteValue(Object value) {
        if (value == null) {
            return 0;
        }

        if (value instanceof Byte) {
            return (Byte) value;
        }

        if (value instanceof Number) {
            return ((Number) value).byteValue();
        }

        if (value instanceof String) {
            String str = (String) value;
            if (str.isEmpty() || "null".equals(str)) {
                return 0;
            }
            return Byte.parseByte(str);
        }

        throw new JSONException("can not cast to byte");
    }

    public static Short toShort(Object value) {
        if (value == null || value instanceof Short) {
            return (Short) value;
        }

        if (value instanceof Number) {
            return ((Number) value).shortValue();
        }

        if (value instanceof String) {
            String str = (String) value;
            if (str.isEmpty() || "null".equals(str)) {
                return null;
            }
            return Short.parseShort(str);
        }

        throw new JSONException("can not cast to byte");
    }

    public static short toShortValue(Object value) {
        if (value == null) {
            return 0;
        }

        if (value instanceof Short) {
            return (Short) value;
        }

        if (value instanceof Number) {
            return (byte) ((Number) value).shortValue();
        }

        if (value instanceof String) {
            String str = (String) value;
            if (str.isEmpty() || "null".equals(str)) {
                return 0;
            }
            return Short.parseShort(str);
        }

        throw new JSONException("can not cast to byte");
    }

    public static int toIntValue(Object value) {
        if (value == null) {
            return 0;
        }

        if (value instanceof Integer) {
            return (Integer) value;
        }

        if (value instanceof Number) {
            return Integer.valueOf(((Number) value).intValue());
        }

        if (value instanceof String) {
            String str = (String) value;
            if (str.isEmpty() || "null".equals(str)) {
                return 0;
            }

            if (str.indexOf('.') != -1) {
                return new BigDecimal(str).intValueExact();
            }

            return Integer.parseInt(str);
        }

        throw new JSONException("can not cast to decimal");
    }

    public static boolean toBooleanValue(Object value) {
        if (value == null) {
            return false;
        }

        if (value instanceof Boolean) {
            return ((Boolean) value).booleanValue();
        }

        if (value instanceof String) {
            String str = (String) value;
            if (str.isEmpty() || "null".equals(str)) {
                return false;
            }
            return Boolean.parseBoolean(str);
        }

        if (value instanceof Number) {
            int intValue = ((Number) value).intValue();
            if (intValue == 1) {
                return true;
            }
            if (intValue == 0) {
                return false;
            }
        }

        throw new JSONException("can not cast to boolean");
    }

    public static Boolean toBoolean(Object value) {
        if (value == null) {
            return null;
        }

        if (value instanceof Boolean) {
            return ((Boolean) value).booleanValue();
        }

        if (value instanceof String) {
            String str = (String) value;
            if (str.isEmpty() || "null".equals(str)) {
                return null;
            }
            return Boolean.parseBoolean(str);
        }

        if (value instanceof Number) {
            int intValue = ((Number) value).intValue();
            if (intValue == 1) {
                return true;
            }
            if (intValue == 0) {
                return false;
            }
        }

        throw new JSONException("can not cast to boolean");
    }

    public static float toFloatValue(Object value) {
        if (value == null) {
            return 0F;
        }

        if (value instanceof Float) {
            return (Float) value;
        }

        if (value instanceof Number) {
            return ((Number) value).floatValue();
        }

        if (value instanceof String) {
            String str = (String) value;
            if (str.isEmpty() || "null".equals(str)) {
                return 0;
            }
            return Float.parseFloat(str);
        }

        throw new JSONException("can not cast to decimal");
    }

    public static Float toFloat(Object value) {
        if (value == null || value instanceof Float) {
            return (Float) value;
        }

        if (value instanceof Number) {
            return ((Number) value).floatValue();
        }

        if (value instanceof String) {
            String str = (String) value;
            if (str.isEmpty() || "null".equals(str)) {
                return null;
            }
            return Float.parseFloat(str);
        }

        throw new JSONException("can not cast to decimal");
    }

    public static double toDoubleValue(Object value) {
        if (value == null) {
            return 0D;
        }

        if (value instanceof Double) {
            return (Double) value;
        }

        if (value instanceof Number) {
            return ((Number) value).doubleValue();
        }

        if (value instanceof String) {
            String str = (String) value;
            if (str.isEmpty() || "null".equals(str)) {
                return 0D;
            }
            return Double.parseDouble(str);
        }

        throw new JSONException("can not cast to decimal");
    }

    public static Double toDouble(Object value) {
        if (value == null || value instanceof Double) {
            return (Double) value;
        }

        if (value instanceof Number) {
            return ((Number) value).doubleValue();
        }

        if (value instanceof String) {
            String str = (String) value;
            if (str.isEmpty() || "null".equals(str)) {
                return null;
            }
            return Double.parseDouble(str);
        }

        throw new JSONException("can not cast to decimal");
    }

    public static int compare(Object a, Object b) {
        if (a.getClass() == b.getClass()) {
            return ((Comparable) a).compareTo(b);
        }

        Class typeA = a.getClass();
        Class typeB = b.getClass();

        if (typeA == BigDecimal.class) {
            if (typeB == Integer.class) {
                b = new BigDecimal((Integer) b);
            } else if (typeB == Long.class) {
                b = new BigDecimal((Long) b);
            } else if (typeB == Float.class) {
                b = new BigDecimal((Float) b);
            } else if (typeB == Double.class) {
                b = new BigDecimal((Double) b);
            } else if (typeB == BigInteger.class) {
                b = new BigDecimal((BigInteger) b);
            }
        } else if (typeA == BigInteger.class) {
            if (typeB == Integer.class) {
                b = BigInteger.valueOf((Integer) b);
            } else if (typeB == Long.class) {
                b = BigInteger.valueOf((Long) b);
            } else if (typeB == Float.class) {
                b = new BigDecimal((Float) b);
                a = new BigDecimal((BigInteger) a);
            } else if (typeB == Double.class) {
                b = new BigDecimal((Double) b);
                a = new BigDecimal((BigInteger) a);
            } else if (typeB == BigDecimal.class) {
                a = new BigDecimal((BigInteger) a);
            }
        } else if (typeA == Long.class) {
            if (typeB == Integer.class) {
                b = new Long((Integer) b);
            } else if (typeB == BigDecimal.class) {
                a = new BigDecimal((Long) a);
            } else if (typeB == Float.class) {
                a = new Float((Long) a);
            } else if (typeB == Double.class) {
                a = new Double((Long) a);
            } else if (typeB == BigInteger.class) {
                a = BigInteger.valueOf((Long) a);
            } else if (typeB == String.class) {
                a = BigDecimal.valueOf((Long) a);
                b = new BigDecimal((String) b);
            }
        } else if (typeA == Integer.class) {
            if (typeB == Long.class) {
                a = new Long((Integer) a);
            } else if (typeB == BigDecimal.class) {
                a = new BigDecimal((Integer) a);
            } else if (typeB == BigInteger.class) {
                a = BigInteger.valueOf((Integer) a);
            } else if (typeB == Float.class) {
                a = new Float((Integer) a);
            } else if (typeB == Double.class) {
                a = new Double((Integer) a);
            } else if (typeB == String.class) {
                a = BigDecimal.valueOf((Integer) a);
                b = new BigDecimal((String) b);
            }
        } else if (typeA == Double.class) {
            if (typeB == Integer.class) {
                b = new Double((Integer) b);
            } else if (typeB == Long.class) {
                b = new Double((Long) b);
            } else if (typeB == Float.class) {
                b = new Double((Float) b);
            } else if (typeB == BigDecimal.class) {
                a = BigDecimal.valueOf((Double) a);
            } else if (typeB == String.class) {
                a = BigDecimal.valueOf((Double) a);
                b = new BigDecimal((String) b);
            } else if (typeB == BigInteger.class) {
                a = BigDecimal.valueOf((Double) a);
                b = new BigDecimal((BigInteger) b);
            }
        } else if (typeA == Float.class) {
            if (typeB == Integer.class) {
                b = new Float((Integer) b);
            } else if (typeB == Long.class) {
                b = new Float((Long) b);
            } else if (typeB == Double.class) {
                a = new Double((Float) a);
            } else if (typeB == BigDecimal.class) {
                a = BigDecimal.valueOf((Float) a);
            } else if (typeB == String.class) {
                a = BigDecimal.valueOf((Float) a);
                b = new BigDecimal((String) b);
            } else if (typeB == BigInteger.class) {
                a = BigDecimal.valueOf((Float) a);
                b = new BigDecimal((BigInteger) b);
            }
        } else if (typeA == String.class) {
            String strA = (String) a;
            if (typeB == Integer.class) {
                NumberFormatException error = null;
                try {
                    a = Integer.parseInt(strA);
                } catch (NumberFormatException ex) {
                    error = ex;
                }
                if (error != null) {
                    try {
                        a = Long.parseLong(strA);
                        b = Long.valueOf((Integer) b);
                        error = null;
                    } catch (NumberFormatException ex) {
                        error = ex;
                    }
                }
                if (error != null) {
                    a = new BigDecimal(strA);
                    b = BigDecimal.valueOf((Integer) b);
                }
            } else if (typeB == Long.class) {
                a = new BigDecimal(strA);
                b = BigDecimal.valueOf((Long) b);
            } else if (typeB == Float.class) {
                a = Float.parseFloat(strA);
            } else if (typeB == Double.class) {
                a = Double.parseDouble(strA);
            } else if (typeB == BigInteger.class) {
                a = new BigInteger(strA);
            } else if (typeB == BigDecimal.class) {
                a = new BigDecimal(strA);
            }
        }

        return ((Comparable) a).compareTo(b);
    }

    public static Object getDefaultValue(Type paramType) {
        if (paramType == int.class) {
            return 0;
        }

        if (paramType == long.class) {
            return 0L;
        }

        if (paramType == float.class) {
            return 0F;
        }

        if (paramType == double.class) {
            return 0D;
        }

        if (paramType == boolean.class) {
            return Boolean.FALSE;
        }

        if (paramType == short.class) {
            return (short) 0;
        }

        if (paramType == byte.class) {
            return (byte) 0;
        }

        if (paramType == char.class) {
            return (char) 0;
        }

        if (paramType == Optional.class) {
            return Optional.empty();
        }

        if (paramType == OptionalInt.class) {
            return OptionalInt.empty();
        }

        if (paramType == OptionalLong.class) {
            return OptionalLong.empty();
        }

        if (paramType == OptionalDouble.class) {
            return OptionalDouble.empty();
        }

        return null;
    }

    public static Class loadClass(String className) {
        if (className.length() >= 192) {
            return null;
        }

        switch (className) {
            case "O":
            case "Object":
            case "java.lang.Object":
                return Object.class;
            case "java.util.Collections$EmptyMap":
                return Collections.EMPTY_MAP.getClass();
            case "java.util.Collections$EmptyList":
                return Collections.EMPTY_LIST.getClass();
            case "java.util.Collections$EmptySet":
                return Collections.EMPTY_SET.getClass();
            case "java.util.Optional":
                return Optional.class;
            case "java.util.OptionalInt":
                return OptionalInt.class;
            case "java.util.OptionalLong":
                return OptionalLong.class;
            case "List":
            case "java.util.List":
                return List.class;
            case "A":
            case "ArrayList":
            case "java.util.ArrayList":
                return ArrayList.class;
            case "LA":
            case "LinkedList":
            case "java.util.LinkedList":
                return LinkedList.class;
            case "Map":
            case "java.util.Map":
                return Map.class;
            case "M":
            case "HashMap":
            case "java.util.HashMap":
                return HashMap.class;
            case "LM":
            case "LinkedHashMap":
            case "java.util.LinkedHashMap":
                return LinkedHashMap.class;
            case "ConcurrentHashMap":
                return ConcurrentHashMap.class;
            case "ConcurrentLinkedQueue":
                return ConcurrentLinkedQueue.class;
            case "ConcurrentLinkedDeque":
                return ConcurrentLinkedDeque.class;
            case "JSONObject":
                return JSONObject.class;
            case "JO1":
                className = "com.alibaba.fastjson.JSONObject";
                break;
            case "Set":
            case "java.util.Set":
                return Set.class;
            case "HashSet":
            case "java.util.HashSet":
                return HashSet.class;
            case "LinkedHashSet":
            case "java.util.LinkedHashSet":
                return LinkedHashSet.class;
            case "TreeSet":
            case "java.util.TreeSet":
                return TreeSet.class;
            case "java.lang.Class":
                return Class.class;
            case "java.lang.Integer":
                return Integer.class;
            case "java.lang.Long":
                return Long.class;
            case "String":
            case "java.lang.String":
                return String.class;
            case "[String":
                return String[].class;
            case "I":
            case "int":
                return int.class;
            case "S":
            case "short":
                return short.class;
            case "J":
            case "long":
                return long.class;
            case "Z":
            case "boolean":
                return boolean.class;
            case "B":
            case "byte":
                return byte.class;
            case "F":
            case "float":
                return float.class;
            case "D":
            case "double":
                return double.class;
            case "C":
            case "char":
                return char.class;
            case "[B":
            case "byte[]":
                return byte[].class;
            case "[S":
            case "short[]":
                return short[].class;
            case "[I":
            case "int[]":
                return int[].class;
            case "[J":
            case "long[]":
                return long[].class;
            case "[F":
            case "float[]":
                return float[].class;
            case "[D":
            case "double[]":
                return double[].class;
            case "[C":
            case "char[]":
                return char[].class;
            case "[Z":
            case "boolean[]":
                return boolean[].class;
            case "[O":
                return Object[].class;
            case "UUID":
                return UUID.class;
            case "Date":
                return Date.class;
            case "Calendar":
                return Calendar.class;
            case "java.io.IOException":
                return java.io.IOException.class;
            case "java.util.Collections$UnmodifiableRandomAccessList":
                return CLASS_UNMODIFIABLE_LIST;
            case "java.util.Arrays$ArrayList":
                return Arrays.asList(1).getClass();
            case "java.util.Collections$SingletonList":
                return CLASS_SINGLE_LIST;
            case "java.util.Collections$SingletonSet":
                return CLASS_SINGLE_SET;
            default:
                break;
        }

        Class mapping = TYPE_MAPPINGS.get(className);
        if (mapping != null) {
            return mapping;
        }

        if (className.startsWith("java.util.ImmutableCollections$")) {
            try {
                return Class.forName(className);
            } catch (ClassNotFoundException e) {
                return CLASS_UNMODIFIABLE_LIST;
            }
        }

        if (className.charAt(0) == 'L' && className.charAt(className.length() - 1) == ';') {
            className = className.substring(1, className.length() - 1);
        }

        if (className.charAt(0) == '[' || className.endsWith("[]")) {
            String itemClassName = className.charAt(0) == '[' ? className.substring(1) : className.substring(0, className.length() - 2);
            Class itemClass = loadClass(itemClassName);
            if (itemClass == null) {
                throw new JSONException("load class error " + className);
            }
            return Array.newInstance(itemClass, 0).getClass();
        }

        ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
        if (contextClassLoader != null) {
            try {
                return contextClassLoader.loadClass(className);
            } catch (ClassNotFoundException ignored) {
            }
        }

        try {
            return JSON.class.getClassLoader().loadClass(className);
        } catch (ClassNotFoundException ignored) {
        }

        try {
            return Class.forName(className);
        } catch (ClassNotFoundException ignored) {
        }

        return null;
    }

    public static Class<?> getArrayClass(Class componentClass) {
        if (componentClass == int.class) {
            return int[].class;
        }
        if (componentClass == byte.class) {
            return byte[].class;
        }
        if (componentClass == short.class) {
            return short[].class;
        }
        if (componentClass == long.class) {
            return long[].class;
        }
        if (componentClass == String.class) {
            return String[].class;
        }
        if (componentClass == Object.class) {
            return Object[].class;
        }
        return Array.newInstance(componentClass, 1).getClass();
    }

    public static Class<?> getClass(Type type) {
        if (type == null) {
            return null;
        }

        if (type.getClass() == Class.class) {
            return (Class<?>) type;
        }

        if (type instanceof ParameterizedType) {
            return getClass(((ParameterizedType) type).getRawType());
        }

        if (type instanceof TypeVariable) {
            Type boundType = ((TypeVariable<?>) type).getBounds()[0];
            if (boundType instanceof Class) {
                return (Class) boundType;
            }
            return getClass(boundType);
        }

        if (type instanceof WildcardType) {
            Type[] upperBounds = ((WildcardType) type).getUpperBounds();
            if (upperBounds.length == 1) {
                return getClass(upperBounds[0]);
            }
        }

        if (type instanceof GenericArrayType) {
            GenericArrayType genericArrayType = (GenericArrayType) type;
            Type componentType = genericArrayType.getGenericComponentType();
            Class<?> componentClass = getClass(componentType);
            return getArrayClass(componentClass);
        }

        return Object.class;
    }

    public static boolean isProxy(Class<?> clazz) {
        for (Class<?> item : clazz.getInterfaces()) {
            String interfaceName = item.getName();
            switch (interfaceName) {
                case "org.springframework.cglib.proxy.Factory":
                case "javassist.util.proxy.ProxyObject":
                case "org.apache.ibatis.javassist.util.proxy.ProxyObject":
                case "org.hibernate.proxy.HibernateProxy":
                case "org.springframework.context.annotation.ConfigurationClassEnhancer$EnhancedConfiguration":
                case "org.mockito.cglib.proxy.Factory":
                case "net.sf.cglib.proxy.Factory":
                    return true;
                default:
                    break;
            }
        }
        return false;
    }

    public static Map getInnerMap(Map object) {
        if (object == null || CLASS_JSON_OBJECT_1x == null || !CLASS_JSON_OBJECT_1x.isInstance(object) || FIELD_JSON_OBJECT_1x_map == null) {
            return object;
        }

        try {
            object = (Map) FIELD_JSON_OBJECT_1x_map.get(object);
        } catch (IllegalAccessException ignore) {
            // ignore
        }

        return object;
    }

    public static boolean isFunction(Class type) {
        if (type.isInterface()) {
            String typeName = type.getName();
            if (typeName.startsWith("java.util.function.")) {
                return true;
            }

            if (type.isAnnotationPresent(FunctionalInterface.class)) {
                return true;
            }
        }

        return false;
    }
}