package org.jcodings.transcode;

import org.jcodings.transcode.specific.From_UTF8_MAC_Transcoder;

import java.util.Arrays;

import static java.lang.Byte.toUnsignedInt;
import static java.lang.Integer.toUnsignedLong;

/**
 * Created by headius on 3/4/14.
 */
public class TranscodeFunctions {
    public static final int BE = 1;
    public static final int LE = 2;

    public static int funSoToCESU8(byte[] statep, byte[] s, int sStart, int l, byte[] o, int oStart, int osize) {
        long scalar = ((toUnsignedInt(s[sStart+0]) & 0x07) << 18) | ((toUnsignedInt(s[sStart+1]) & 0x3F) << 12) | ((toUnsignedInt(s[sStart+2]) & 0x3F) << 6) | (toUnsignedInt(s[sStart+3]) & 0x3F);
        scalar -= 0x10000;
        o[oStart+0] = (byte)0xED;
        o[oStart+1] = (byte)(0xA0 | (scalar >> 16));
        o[oStart+2] = (byte)(0x80 | ((scalar >> 10) & 0x3F));
        o[oStart+3] = (byte)0xED;
        o[oStart+4] = (byte)(0xB0 | ((scalar >> 6) & 0x0F));
        o[oStart+5] = (byte)(0x80 | (scalar & 0x3F));
        return 6;
    }

    public static int funSoFromCESU8(byte[] statep, byte[] s, int sStart, int l, byte[] o, int oStart, int osize) {
        long scalar = (((toUnsignedInt(s[sStart+1]) & 0x0F) << 16) | ((toUnsignedInt(s[sStart+2]) & 0x3F) << 10) | ((toUnsignedInt(s[sStart+4]) & 0x0F) << 6) | (toUnsignedInt(s[sStart+5]) & 0x3F)) + 0x10000;
        o[oStart+0] = (byte)(0xF0 | (scalar >> 18));
        o[oStart+1] = (byte)(0x80 | ((scalar >> 12) & 0x3F));
        o[oStart+2] = (byte)(0x80 | ((scalar >> 6) & 0x3F));
        o[oStart+3] = (byte)(0x80 | (scalar & 0x3F));
        return 4;
    }

    public static int funSoToUTF16(byte[] statep, byte[] sBytes, int sStart, int l, byte[] o, int oStart, int osize) {
        int sp = 0;
        if (statep[sp] == 0) {
            o[oStart++] = (byte)0xFE;
            o[oStart++] = (byte)0xFF;
            statep[sp] = (byte)1;
            return 2 + funSoToUTF16BE(statep, sBytes, sStart, l, o, oStart, osize);
        }
        return funSoToUTF16BE(statep, sBytes, sStart, l, o, oStart, osize);
    }

    public static int funSoToUTF16BE(byte[] statep, byte[] s, int sStart, int l, byte[] o, int oStart, int osize) {
        int s0 = toUnsignedInt(s[sStart]);
        int s1, s2, s3;

        if ((s0 & 0x80) == 0) {
            o[oStart] = 0x00;
            o[oStart + 1] = (byte)s0;
            return 2;
        } else if ((s0 & 0xE0) == 0xC0) {
            s1 = toUnsignedInt(s[sStart+1]);
            o[oStart] = (byte)((s0 >> 2) & 0x07);
            o[oStart + 1] = (byte)(((s0 & 0x03) << 6) | (s1 & 0x3F));
            return 2;
        } else if ((s0 & 0xF0) == 0xE0) {
            s1 = toUnsignedInt(s[sStart+1]);
            s2 = toUnsignedInt(s[sStart+2]);
            o[oStart] = (byte)((s0 << 4) | ((s1 >> 2) ^ 0x20));
            o[oStart + 1] = (byte)((s1 << 6) | (s2 ^ 0x80));
            return 2;
        } else {
            s1 = toUnsignedInt(s[sStart+1]);
            s2 = toUnsignedInt(s[sStart+2]);
            s3 = toUnsignedInt(s[sStart+3]);
            int w = (((s0 & 0x07) << 2) | ((s1 >> 4) & 0x03)) - 1;
            o[oStart] = (byte)(0xD8 | (w >> 2));
            o[oStart + 1] = (byte)((w << 6) | ((s1 & 0x0F) << 2) | ((s2 >> 4) - 8));
            o[oStart + 2] = (byte)(0xDC | ((s2 >> 2) & 0x03));
            o[oStart + 3] = (byte)((s2 << 6) | (s3 & ~0x80));
            return 4;
        }
    }

    public static int funSoToUTF16LE(byte[] statep, byte[] s, int sStart, int l, byte[] o, int oStart, int osize) {
        int s0 = toUnsignedInt(s[sStart]);
        int s1;
        if ((s0 & 0x80) == 0) {
            o[oStart + 1] = (byte)0x00;
            o[oStart] = (byte)s0;
            return 2;
        } else if ((s0 & 0xE0) == 0xC0) {
            s1 = toUnsignedInt(s[sStart+1]);
            o[oStart + 1] = (byte)((s0 >> 2) & 0x07);
            o[oStart] = (byte)(((s0 & 0x03) << 6) | (s1 & 0x3F));
            return 2;
        } else if ((s0 & 0xF0) == 0xE0) {
            s1 = toUnsignedInt(s[sStart+1]);
            int s2 = toUnsignedInt(s[sStart+2]);
            o[oStart + 1] = (byte)((s0 << 4) | ((s1 >> 2) ^ 0x20));
            o[oStart] = (byte)((s1 << 6) | (s2 ^ 0x80));
            return 2;
        } else {
            s1 = toUnsignedInt(s[sStart+1]);
            int s2 = toUnsignedInt(s[sStart+2]);
            int s3 = toUnsignedInt(s[sStart+3]);
            int w = (((s0 & 0x07) << 2) | ((s1 >> 4) & 0x03)) - 1;
            o[oStart + 1] = (byte)(0xD8 | (w >> 2));
            o[oStart] = (byte)((w << 6) | ((s1 & 0x0F) << 2) | ((s2 >> 4) - 8));
            o[oStart + 3] = (byte)(0xDC | ((s2 >> 2) & 0x03));
            o[oStart + 2] = (byte)((s2 << 6) | (s3 & ~0x80));
            return 4;
        }
    }

    public static int funSoToUTF32(byte[] statep, byte[] s, int sStart, int l, byte[] o, int oStart, int osize) {
        int sp = 0;
        if (statep[sp] == 0) {
            o[oStart++] = 0x00;
            o[oStart++] = 0x00;
            o[oStart++] = (byte)0xFE;
            o[oStart++] = (byte)0xFF;
            statep[sp] = 1;
            return 4 + funSoToUTF32BE(statep, s, sStart, l, o, oStart, osize);
        }
        return funSoToUTF32BE(statep, s, sStart, l, o, oStart, osize);
    }

    public static int funSoToUTF32BE(byte[] statep, byte[] s, int sStart, int l, byte[] o, int oStart, int osize) {
        int s0 = toUnsignedInt(s[sStart]);
        int s1, s2, s3;
        o[oStart] = 0;
        if ((s0 & 0x80) == 0) {
            o[oStart + 1] = o[oStart + 2] = 0x00;
            o[oStart + 3] = (byte)s0;
        } else if ((s0 & 0xE0) == 0xC0) {
            s1 = toUnsignedInt(s[sStart+1]);
            o[oStart + 1] = 0x00;
            o[oStart + 2] = (byte)((s0 >> 2) & 0x07);
            o[oStart + 3] = (byte)(((s0 & 0x03) << 6) | (s1 & 0x3F));
        } else if ((s0 & 0xF0) == 0xE0) {
            s1 = toUnsignedInt(s[sStart+1]);
            s2 = toUnsignedInt(s[sStart+2]);
            o[oStart + 1] = 0x00;
            o[oStart + 2] = (byte)((s0 << 4) | ((s1 >> 2) ^ 0x20));
            o[oStart + 3] = (byte)((s1 << 6) | (s2 ^ 0x80));
        } else {
            s1 = toUnsignedInt(s[sStart+1]);
            s2 = toUnsignedInt(s[sStart+2]);
            s3 = toUnsignedInt(s[sStart+3]);
            o[oStart + 1] = (byte)(((s0 & 0x07) << 2) | ((s1 >> 4) & 0x03));
            o[oStart + 2] = (byte)(((s1 & 0x0F) << 4) | ((s2 >> 2) & 0x0F));
            o[oStart + 3] = (byte)(((s2 & 0x03) << 6) | (s3 & 0x3F));
        }
        return 4;
    }

    public static int funSoToUTF32LE(byte[] statep, byte[] s, int sStart, int l, byte[] o, int oStart, int osize) {
        o[oStart+3] = 0;
        int s0 = toUnsignedInt(s[sStart]);
        if ((s0 & 0x80) == 0) {
            o[oStart + 2] = o[oStart+1] = 0x00;
            o[oStart] = (byte)s0;
        } else if ((s[sStart] & 0xE0) == 0xC0) {
            int s1 = toUnsignedInt(s[sStart+1]);
            o[oStart + 2] = 0x00;
            o[oStart + 1] = (byte)((s0 >> 2) & 0x07);
            o[oStart] = (byte)(((s0 & 0x03) << 6) | (s1 & 0x3F));
        } else if ((s[sStart] & 0xF0) == 0xE0) {
            int s1 = toUnsignedInt(s[sStart+1]);
            int s2 = toUnsignedInt(s[sStart+2]);
            o[oStart + 2] = 0x00;
            o[oStart + 1] = (byte)((s0 << 4) | ((s1 >> 2) ^ 0x20));
            o[oStart] = (byte)((s1 << 6) | (s2 ^ 0x80));
        } else {
            int s1 = toUnsignedInt(s[sStart+1]);
            int s2 = toUnsignedInt(s[sStart+2]);
            int s3 = toUnsignedInt(s[sStart+3]);
            o[oStart + 2] = (byte)(((s0 & 0x07) << 2) | ((s1 >> 4) & 0x03));
            o[oStart + 1] = (byte)(((s1 & 0x0F) << 4) | ((s2 >> 2) & 0x0F));
            o[oStart] = (byte)(((s2 & 0x03) << 6) | (s3 & 0x3F));
        }
        return 4;
    }

    public static int funSiFromUTF32(byte[] statep, byte[] s, int sStart, int l) {
        int s0 = toUnsignedInt(s[sStart]);
        int s1 = toUnsignedInt(s[sStart+1]);
        int s2 = toUnsignedInt(s[sStart+2]);
        int s3;
        byte[] sp = statep;

        switch (sp[0]) {
            case 0:
                s3 = toUnsignedInt(s[sStart+3]);
                if (s0 == 0 && s1 == 0 && s2 == 0xFE && s3 == 0xFF) {
                    sp[0] = BE;
                    return TranscodingInstruction.ZERObt;
                } else if (s0 == 0xFF && s1 == 0xFE && s2 == 0 && s3 == 0) {
                    sp[0] = LE;
                    return TranscodingInstruction.ZERObt;
                }
                break;
            case BE:
                if (s0 == 0 && ((0 < s1 && s1 <= 0x10)) ||
                        (s1 == 0 && (s2 < 0xD8 || 0xDF < s2))) {
                    return TranscodingInstruction.FUNso;
                }
                break;
            case LE:
                s3 = toUnsignedInt(s[sStart+3]);
                if (s3 == 0 && ((0 < s2 && s2 <= 0x10) ||
                        (s2 == 0 && (s1 < 0xD8 || 0xDF < s1))))
                    return TranscodingInstruction.FUNso;
                break;
        }
        return TranscodingInstruction.INVALID;
    }

    public static int funSoFromUTF32(byte[] statep, byte[] s, int sStart, int l, byte[] o, int oStart, int osize) {
        switch (statep[0]) {
            case BE:
                return funSoFromUTF32BE(statep, s, sStart, l, o, oStart, osize);
            case LE:
                return funSoFromUTF32LE(statep, s, sStart, l, o, oStart, osize);
        }
        return 0;
    }

    public static int funSoFromUTF32BE(byte[] statep, byte[] s, int sStart, int l, byte[] o, int oStart, int osize) {
        int s1 = toUnsignedInt(s[sStart+1]);
        int s2 = toUnsignedInt(s[sStart+2]);
        int s3 = toUnsignedInt(s[sStart+3]);
        if (s1 == 0) {
            if (s2 == 0 && s3 < 0x80) {
                o[oStart] = (byte)s3;
                return 1;
            } else if (s2 < 0x08) {
                o[oStart] = (byte)(0xC0 | (s2 << 2) | (s3 >> 6));
                o[oStart + 1] = (byte)(0x80 | (s3 & 0x3F));
                return 2;
            } else {
                o[oStart] = (byte)(0xE0 | (s2 >> 4));
                o[oStart + 1] = (byte)(0x80 | ((s2 & 0x0F) << 2) | (s3 >> 6));
                o[oStart + 2] = (byte)(0x80 | (s3 & 0x3F));
                return 3;
            }
        } else {
            o[oStart] = (byte)(0xF0 | (s1 >> 2));
            o[oStart + 1] = (byte)(0x80 | ((s1 & 0x03) << 4) | (s2 >> 4));
            o[oStart + 2] = (byte)(0x80 | ((s2 & 0x0F) << 2) | (s3 >> 6));
            o[oStart + 3] = (byte)(0x80 | (s3 & 0x3F));
            return 4;
        }
    }

    public static int funSoFromUTF32LE(byte[] statep, byte[] s, int sStart, int l, byte[] o, int oStart, int osize) {
        int s0 = toUnsignedInt(s[sStart]);
        int s1 = toUnsignedInt(s[sStart+1]);
        int s2 = toUnsignedInt(s[sStart+2]);
        if (s2 == 0) {
            if (s1 == 0 && s0 < 0x80) {
                o[oStart] = (byte)s0;
                return 1;
            } else if (s1 < 0x08) {
                o[oStart] = (byte)(0xC0 | (s1 << 2) | (s0 >> 6));
                o[oStart + 1] = (byte)(0x80 | (s0 & 0x3F));
                return 2;
            } else {
                o[oStart] = (byte)(0xE0 | (s1 >> 4));
                o[oStart + 1] = (byte)(0x80 | ((s1 & 0x0F) << 2) | (s0 >> 6));
                o[oStart + 2] = (byte)(0x80 | (s0 & 0x3F));
                return 3;
            }
        } else {
            o[oStart] = (byte)(0xF0 | (s2 >> 2));
            o[oStart + 1] = (byte)(0x80 | ((s2 & 0x03) << 4) | (s1 >> 4));
            o[oStart + 2] = (byte)(0x80 | ((s1 & 0x0F) << 2) | (s0 >> 6));
            o[oStart + 3] = (byte)(0x80 | (s0 & 0x3F));
            return 4;
        }
    }

    public static final int from_UTF_16BE_D8toDB_00toFF = Transcoding.WORDINDEX2INFO(39);
    public static final int from_UTF_16LE_00toFF_D8toDB = Transcoding.WORDINDEX2INFO(5);

    public static int funSiFromUTF16(byte[] statep, byte[] s, int sStart, int l) {
        int s0 = toUnsignedInt(s[sStart]);
        int s1;
        byte[] sp = statep;

        switch (sp[0]) {
            case 0:
                s1 = toUnsignedInt(s[sStart+1]);
                if (s0 == 0xFE && s1 == 0xFF) {
                    sp[0] = BE;
                    return TranscodingInstruction.ZERObt;
                } else if (s0 == 0xFF && s1 == 0xFE) {
                    sp[0] = LE;
                    return TranscodingInstruction.ZERObt;
                }
                break;
            case BE:
                if (s0 < 0xD8 || 0xDF < s0) {
                    return TranscodingInstruction.FUNso;
                } else if (s0 <= 0xDB) {
                    return from_UTF_16BE_D8toDB_00toFF;
                }
                break;
            case LE:
                s1 = toUnsignedInt(s[sStart+1]);
                if (s1 < 0xD8 || 0xDF < s1) {
                    return TranscodingInstruction.FUNso;
                } else if (s1 <= 0xDB) {
                    return from_UTF_16LE_00toFF_D8toDB;
                }
                break;
        }
        return TranscodingInstruction.INVALID;
    }

    public static int funSoFromUTF16(byte[] statep, byte[] s, int sStart, int l, byte[] o, int oStart, int osize) {
        switch (statep[0]) {
            case BE:
                return funSoFromUTF16BE(statep, s, sStart, l, o, oStart, osize);
            case LE:
                return funSoFromUTF16LE(statep, s, sStart, l, o, oStart, osize);
        }
        return 0;
    }

    public static int funSoFromUTF16BE(byte[] statep, byte[] s, int sStart, int l, byte[] o, int oStart, int osize) {
        int s0 = toUnsignedInt(s[sStart]);
        int s1 = toUnsignedInt(s[sStart+1]);
        int s2, s3;
        if (s0 == 0 && s1 < 0x80) {
            o[oStart] = (byte)s1;
            return 1;
        } else if (s0 < 0x08) {
            o[oStart] = (byte)(0xC0 | (s0 << 2) | (s1 >> 6));
            o[oStart + 1] = (byte)(0x80 | (s1 & 0x3F));
            return 2;
        } else if ((s0 & 0xF8) != 0xD8) {
            o[oStart] = (byte)(0xE0 | (s0 >> 4));
            o[oStart + 1] = (byte)(0x80 | ((s0 & 0x0F) << 2) | (s1 >> 6));
            o[oStart + 2] = (byte)(0x80 | (s1 & 0x3F));
            return 3;
        } else {
            s2 = toUnsignedInt(s[sStart+2]);
            s3 = toUnsignedInt(s[sStart+3]);
            long u = (((s0 & 0x03) << 2) | (s1 >> 6)) + 1;
            o[oStart] = (byte)(0xF0 | (u >> 2));
            o[oStart + 1] = (byte)(0x80 | ((u & 0x03) << 4) | (((s1 >> 2) & 0x0F)));
            o[oStart + 2] = (byte)(0x80 | ((s1 & 0x03) << 4) | ((s2 & 0x03) << 2) | (s3 >> 6));
            o[oStart + 3] = (byte)(0x80 | (s3 & 0x3F));
            return 4;
        }
    }

    public static int funSoFromUTF16LE(byte[] statep, byte[] s, int sStart, int l, byte[] o, int oStart, int osize) {
        int s0 = toUnsignedInt(s[sStart]);
        int s1 = toUnsignedInt(s[sStart+1]);
        if (s1 == 0 && s0 < 0x80) {
            o[oStart] = (byte)s0;
            return 1;
        } else if (s1 < 0x08) {
            o[oStart] = (byte)(0xC0 | (s1 << 2) | (s0 >> 6));
            o[oStart + 1] = (byte)(0x80 | (s0 & 0x3F));
            return 2;
        } else if ((s1 & 0xF8) != 0xD8) {
            o[oStart] = (byte)(0xE0 | (s1 >> 4));
            o[oStart + 1] = (byte)(0x80 | ((s1 & 0x0F) << 2) | (s0 >> 6));
            o[oStart + 2] = (byte)(0x80 | (s0 & 0x3F));
            return 3;
        } else {
            int s2 = toUnsignedInt(s[sStart+2]);
            int s3 = toUnsignedInt(s[sStart+3]);
            long u = (((s1 & 0x03) << 2) | (s0 >> 6)) + 1;
            o[oStart] = (byte)(0xF0 | (u >> 2));
            o[oStart + 1] = (byte)(0x80 | ((u & 0x03) << 4) | ((s0 >> 2) & 0x0F));
            o[oStart + 2] = (byte)(0x80 | ((s0 & 0x03) << 4) | ((s3 & 0x03) << 2) | (s2 >> 6));
            o[oStart + 3] = (byte)(0x80 | (s2 & 0x3F));
            return 4;
        }
    }

    public static int funSoEucjp2Sjis(byte[] statep, byte[] s, int sStart, int _l, byte[] o, int oStart, int osize) {
        int s0 = toUnsignedInt(s[sStart]);
        int s1 = toUnsignedInt(s[sStart+1]);
        if (s0 == 0x8e) {
            o[oStart] = (byte)s1;
            return 1;
        } else {
            int h, m, l;
            m = s0 & 1;
            h = (s0 + m) >> 1;
            h += s0 < 0xdf ? 0x30 : 0x70;
            l = s1 - m * 94 - 3;
            if (0x7f <= l) {
                l++;
            }
            o[oStart] = (byte)h;
            o[oStart+1] = (byte)l;
            return 2;
        }
    }

    public static int funSoSjis2Eucjp(byte[] statep, byte[] s, int sStart, int _l, byte[] o, int oStart, int osize) {
        int s0 = toUnsignedInt(s[sStart]);
        if (_l == 1) {
            o[oStart] = (byte)0x8E;
            o[oStart+1] = (byte)s0;
            return 2;
        } else {
            int h, l;
            h = s0;
            l = Byte.toUnsignedInt(s[sStart+1]);
            if (0xe0 <= h) {
                h -= 64;
            }
            l += l < 0x80 ? 0x61 : 0x60;
            h = h * 2 - 0x61;
            if (0xfe < l) {
                l -= 94;
                h += 1;
            }
            o[oStart] = (byte)h;
            o[oStart+1] = (byte)l;
            return 2;
        }
    }

    public static int funSoFromGB18030(byte[] statep, byte[] s, int sStart, int l, byte[] o, int oStart, int osize)
    {
        int s0 = toUnsignedInt(s[sStart]);
        int s1 = toUnsignedInt(s[sStart+1]);
        int s2 = toUnsignedInt(s[sStart+2]);
        int s3 = toUnsignedInt(s[sStart+3]);
        long u = toUnsignedLong((s0 - 0x90) * 10 * 126 * 10 + (s1 - 0x30) * 126 * 10 + (s2 - 0x81) * 10 + (s3 - 0x30) + 0x10000);
        o[oStart] = (byte)(0xF0 | (u >>> 18));
        o[oStart+1] = (byte)(0x80 | ((u >>> 12) & 0x3F));
        o[oStart+2] = (byte)(0x80 | ((u >>> 6) & 0x3F));
        o[oStart+3] = (byte)(0x80 | (u & 0x3F));
        return 4;
    }

    public static int funSioFromGB18030(byte[] statep, byte[] s, int sStart, int l, int info, byte[] o, int oStart, int osize)
    {
        int s0 = toUnsignedInt(s[sStart]);
        int s1 = toUnsignedInt(s[sStart+1]);
        int diff = info >> 8;
        long u;    /* Unicode Scalar Value */
        if ((diff & 0x20000) != 0) { /* GB18030 4 bytes */
            int s2 = toUnsignedInt(s[sStart+2]);
            int s3 = toUnsignedInt(s[sStart+3]);
            u = toUnsignedLong(((s0 * 10 + s1) * 126 + s2) * 10 + s3 - diff - 0x170000);
        }
        else { /* GB18030 2 bytes */
            u = toUnsignedLong(s0 * 256 + s1 + 24055 - diff);
        }
        o[oStart] = (byte)(0xE0 | (u >>> 12));
        o[oStart+1] = (byte)(0x80 | ((u >>> 6) & 0x3F));
        o[oStart+2] = (byte)(0x80 | (u & 0x3F));
        return 3;
    }

    public static int funSoToGB18030(byte[] statep, byte[] s, int sStart, int l, byte[] o, int oStart, int osize)
    {
        long s0 = toUnsignedInt(s[sStart]);
        long s1 = toUnsignedInt(s[sStart+1]);
        long s2 = toUnsignedInt(s[sStart+2]);
        long s3 = toUnsignedInt(s[sStart+3]);
        long u = ((s0 & 0x07) << 18) | ((s1 & 0x3F) << 12) | ((s2 & 0x3F) << 6) | (s3 & 0x3F);
        u -= 0x10000;
        o[oStart+3] = (byte)(0x30 + (u % 10));
        u /= 10;
        o[oStart+2] = (byte)(0x81 + (u % 126));
        u /= 126;
        o[oStart+1] = (byte)(0x30 + (u % 10));
        o[oStart] = (byte)(0x90 + u / 10);
        return 4;
    }

    public static int funSioToGB18030(byte[] statep, byte[] s, int sStart, int l, int info, byte[] o, int oStart, int osize)
    {
        long s0 = toUnsignedInt(s[sStart]);
        long s1 = toUnsignedInt(s[sStart+1]);
        long s2 = toUnsignedInt(s[sStart+2]);
        long diff = info >>> 8;
        long u;    /* Unicode Scalar Value */

        u = ((s0 & 0x0F) << 12) | ((s1 & 0x3F) << 6) | (s2 & 0x3F);

        if ((diff & 0x20000) != 0) { /* GB18030 4 bytes */
            u += (diff + 0x170000);
            u -= 1688980;
            u += 0x2;
            o[oStart+3] = (byte)(0x30 + (u % 10));
            u /= 10;
            u += 0x32;
            o[oStart+2] = (byte)(0x81 + (u % 126));
            u /= 126;
            u += 0x1;
            o[oStart+1] = (byte)(0x30 + (u % 10));
            u /= 10;
            o[oStart] = (byte)(0x81 + u);
            return 4;
        }
        else { /* GB18030 2 bytes */
            u += (diff - 24055);
            o[oStart+1] = (byte)(u % 256);
            o[oStart] = (byte)(u / 256);
            return 2;
        }
    }

    public static int iso2022jpInit(byte[] state) {
        state[0] = G0_ASCII;
        return 0;
    }

    public static final byte G0_ASCII = 0;
    public static final byte G0_JISX0208_1978 = 1;
    public static final byte G0_JISX0208_1983 = 2;
    public static final byte G0_JISX0201_KATAKANA = 3;

    public static final int EMACS_MULE_LEADING_CODE_JISX0208_1978 = 0220;
    public static final int EMACS_MULE_LEADING_CODE_JISX0208_1983 = 0222;

    public static final byte[] tbl0208 = {
                    (byte)0x21, (byte)0x23, (byte)0x21, (byte)0x56, (byte)0x21, (byte)0x57, (byte)0x21, (byte)0x22, (byte)0x21, (byte)0x26, (byte)0x25, (byte)0x72, (byte)0x25, (byte)0x21, (byte)0x25, (byte)0x23,
                    (byte)0x25, (byte)0x25, (byte)0x25, (byte)0x27, (byte)0x25, (byte)0x29, (byte)0x25, (byte)0x63, (byte)0x25, (byte)0x65, (byte)0x25, (byte)0x67, (byte)0x25, (byte)0x43, (byte)0x21, (byte)0x3C,
                    (byte)0x25, (byte)0x22, (byte)0x25, (byte)0x24, (byte)0x25, (byte)0x26, (byte)0x25, (byte)0x28, (byte)0x25, (byte)0x2A, (byte)0x25, (byte)0x2B, (byte)0x25, (byte)0x2D, (byte)0x25, (byte)0x2F,
                    (byte)0x25, (byte)0x31, (byte)0x25, (byte)0x33, (byte)0x25, (byte)0x35, (byte)0x25, (byte)0x37, (byte)0x25, (byte)0x39, (byte)0x25, (byte)0x3B, (byte)0x25, (byte)0x3D, (byte)0x25, (byte)0x3F,
                    (byte)0x25, (byte)0x41, (byte)0x25, (byte)0x44, (byte)0x25, (byte)0x46, (byte)0x25, (byte)0x48, (byte)0x25, (byte)0x4A, (byte)0x25, (byte)0x4B, (byte)0x25, (byte)0x4C, (byte)0x25, (byte)0x4D,
                    (byte)0x25, (byte)0x4E, (byte)0x25, (byte)0x4F, (byte)0x25, (byte)0x52, (byte)0x25, (byte)0x55, (byte)0x25, (byte)0x58, (byte)0x25, (byte)0x5B, (byte)0x25, (byte)0x5E, (byte)0x25, (byte)0x5F,
                    (byte)0x25, (byte)0x60, (byte)0x25, (byte)0x61, (byte)0x25, (byte)0x62, (byte)0x25, (byte)0x64, (byte)0x25, (byte)0x66, (byte)0x25, (byte)0x68, (byte)0x25, (byte)0x69, (byte)0x25, (byte)0x6A,
                    (byte)0x25, (byte)0x6B, (byte)0x25, (byte)0x6C, (byte)0x25, (byte)0x6D, (byte)0x25, (byte)0x6F, (byte)0x25, (byte)0x73, (byte)0x21, (byte)0x2B, (byte)0x21, (byte)0x2C};

    public static int funSoCp50220Encoder(byte[] statep, byte[] s, int sStart, int l, byte[] o, int oStart, int oSize) {
        int s0;
        int s1;
        int output0 = oStart;
        byte[] sp = statep;

        if (sp[0] == G0_JISX0201_KATAKANA) {
            int c = sp[2] & 0x7F;
            int p = (c - 0x21) * 2;
            byte[] pBytes = tbl0208;
            if (sp[1] != G0_JISX0208_1983) {
                o[oStart++] = 0x1B;
                o[oStart++] = (byte)'$';
                o[oStart++] = (byte)'B';
            }
            sp[0] = G0_JISX0208_1983;
            o[oStart++] = pBytes[p++];
            s0 = toUnsignedInt(s[sStart]);
            s1 = toUnsignedInt(s[sStart+1]);
            if (l == 2 && s0 == 0x8E) {
                if (s1 == 0xDE) {
                    o[oStart++] = (byte)(pBytes[p] + 1);
                    return oStart - output0;
                } else if (s1 == 0xDF && (0x4A <= c && c <= 0x4E)) {
                    o[oStart++] = (byte)(pBytes[p] + 2);
                    return oStart - output0;
                }
            }
            o[oStart++] = pBytes[p];
        }

        s0 = toUnsignedInt(s[sStart]);
        if (l == 2 && s0 == 0x8E) {
            s1 = toUnsignedInt(s[sStart+1]);
            int p = (s1 - 0xA1) * 2;
            byte[] pBytes = tbl0208;
            if ((0xA1 <= s1 && s1 <= 0xB5) ||
                    (0xC5 <= s1 && s1 <= 0xC9) ||
                    (0xCF <= s1 && s1 <= 0xDF)) {
                if (sp[0] != G0_JISX0208_1983) {
                    o[oStart++] = 0x1b;
                    o[oStart++] = '$';
                    o[oStart++] = 'B';
                    sp[0] = G0_JISX0208_1983;
                }
                o[oStart++] = pBytes[p++];
                o[oStart++] = pBytes[p];
                return oStart - output0;
            }

            sp[2] = (byte)s1;
            sp[1] = sp[0];
            sp[0] = G0_JISX0201_KATAKANA;
            return oStart - output0;
        }

        oStart += funSoCp5022xEncoder(statep, s, sStart, l, o, oStart, oSize);
        return oStart - output0;
    }

    public static int funSoCp5022xEncoder(byte[] statep, byte[] s, int sStart, int l, byte[] o, int oStart, int oSize) {
        int s0, s1;
        byte[] sp = statep;
        int output0 = oStart;
        int newstate;

        if (l == 1) {
            newstate = G0_ASCII;
        } else if (toUnsignedInt(s[sStart]) == 0x8E) {
            sStart++;
            l = 1;
            newstate = G0_JISX0201_KATAKANA;
        } else {
            newstate = G0_JISX0208_1983;
        }

        if (sp[0] != newstate) {
            if (newstate == G0_ASCII) {
                o[oStart++] = 0x1b;
                o[oStart++] = '(';
                o[oStart++] = 'B';
            }
            else if (newstate == G0_JISX0201_KATAKANA) {
                o[oStart++] = 0x1b;
                o[oStart++] = '(';
                o[oStart++] = 'I';
            }
            else {
                o[oStart++] = 0x1b;
                o[oStart++] = '$';
                o[oStart++] = 'B';
            }
            sp[0] = (byte)newstate;
        }

        s0 = toUnsignedInt(s[sStart]);
        if (l == 1) {
            o[oStart++] = (byte)(s0 & 0x7f);
        }
        else {
            s1 = toUnsignedInt(s[sStart+1]);
            o[oStart++] = (byte)(s0 & 0x7f);
            o[oStart++] = (byte)(s1 & 0x7f);
        }

        return oStart - output0;
    }

    public static int finishCp50220Encoder(byte[] statep, byte[] o, int oStart, int size) {
        byte[] sp = statep;
        int output0 = oStart;

        if (sp[0] == G0_ASCII) return 0;

        if (sp[0] == G0_JISX0201_KATAKANA) {
            int c = sp[2] & 0x7F;
            int p = (c - 0x21) * 2;
            byte[] pBytes = tbl0208;
            if (sp[1] != G0_JISX0208_1983) {
                o[oStart++] = 0x1b;
                o[oStart++] = '$';
                o[oStart++] = 'B';
            }
            sp[0] = G0_JISX0208_1983;
            o[oStart++] = pBytes[p++];
            o[oStart++] = pBytes[p];
        }

        o[oStart++] = 0x1b;
        o[oStart++] = '(';
        o[oStart++] = 'B';
        sp[0] = G0_ASCII;

        return oStart - output0;
    }

    public static int iso2022jpEncoderResetSequenceSize(byte[] statep) {
        byte[] sp = statep;
        if (sp[0] != G0_ASCII) return 3;
        return 0;
    }

    public static final int iso2022jp_decoder_jisx0208_rest = Transcoding.WORDINDEX2INFO(16);

    public static int funSiIso50220jpDecoder(byte[] statep, byte[] s, int sStart, int l) {
        int s0 = toUnsignedInt(s[sStart]);
        byte[] sp = statep;
        if (sp[0] == G0_ASCII)
        return TranscodingInstruction.NOMAP;
        else if (0x21 <= s0 && s0 <= 0x7e)
            return iso2022jp_decoder_jisx0208_rest;
        else
            return TranscodingInstruction.INVALID;
    }

    public static int funSoIso2022jpDecoder(byte[] statep, byte[] s, int sStart, int l, byte[] o, int oStart, int oSize) {
        int s0 = toUnsignedInt(s[sStart]);
        int s1 = toUnsignedInt(s[sStart+1]);
        byte[] sp = statep;
        if (s0 == 0x1b) {
            if (s1 == '(') {
                switch (toUnsignedInt(s[sStart+l-1])) {
                    case 'B':
                    case 'J':
                        sp[0] = G0_ASCII;
                        break;
                }
            }
            else {
                switch (s[sStart+l-1]) {
                    case '@':
                        sp[0] = G0_JISX0208_1978;
                        break;

                    case 'B':
                        sp[0] = G0_JISX0208_1983;
                        break;
                }
            }
            return 0;
        }
        else {
            if (sp[0] == G0_JISX0208_1978) {
                o[oStart] = (byte)EMACS_MULE_LEADING_CODE_JISX0208_1978;
            } else {
                o[oStart] = (byte)EMACS_MULE_LEADING_CODE_JISX0208_1983;
            }
            o[oStart+1] = (byte)(s0 | 0x80);
            o[oStart+2] = (byte)(s1 | 0x80);
            return 3;
        }
    }

    public static int funSoStatelessIso2022jpToEucjp(byte[] statep, byte[] s, int sStart, int l, byte[] o, int oStart, int oSize) {
        o[oStart] = s[sStart+1];
        o[oStart+1] = s[sStart+2];
        return 2;
    }

    public static int funSoEucjpToStatelessIso2022jp(byte[] statep, byte[] s, int sStart, int l, byte[] o, int oStart, int oSize) {
        o[oStart] = (byte)EMACS_MULE_LEADING_CODE_JISX0208_1983;
        o[oStart+1] = s[sStart];
        o[oStart+2] = s[sStart+1];
        return 3;
    }

    public static int funSoIso2022jpEncoder(byte[] statep, byte[] s, int sStart, int l, byte[] o, int oStart, int oSize) {
        byte[] sp = statep;
        int output0 = oStart;
        int newstate;

        if (l == 1)
            newstate = G0_ASCII;
        else if (toUnsignedInt(s[sStart]) == EMACS_MULE_LEADING_CODE_JISX0208_1978)
            newstate = G0_JISX0208_1978;
        else
            newstate = G0_JISX0208_1983;

        if (sp[0] != newstate) {
            if (newstate == G0_ASCII) {
                o[oStart++] = 0x1b;
                o[oStart++] = '(';
                o[oStart++] = 'B';
            }
            else if (newstate == G0_JISX0208_1978) {
                o[oStart++] = 0x1b;
                o[oStart++] = '$';
                o[oStart++] = '@';
            }
            else {
                o[oStart++] = 0x1b;
                o[oStart++] = '$';
                o[oStart++] = 'B';
            }
            sp[0] = (byte)newstate;
        }

        if (l == 1) {
            o[oStart++] = (byte)(s[sStart] & 0x7f);
        }
        else {
            o[oStart++] = (byte)(s[sStart+1] & 0x7f);
            o[oStart++] = (byte)(s[sStart+2] & 0x7f);
        }

        return oStart - output0;
    }

    public static int finishIso2022jpEncoder(byte[] statep, byte[] o, int oStart, int oSize) {
        byte[] sp = statep;
        int output0 = oStart;

        if (sp[0] == G0_ASCII) return 0;

        o[oStart++] = 0x1b;
        o[oStart++] = '(';
        o[oStart++] = 'B';
        sp[0] = G0_ASCII;

        return oStart - output0;
    }

    public static int funSiCp50221Decoder(byte[] statep, byte[] s, int sStart, int l) {
        byte[] sp = statep;
        int c;
        int s0 = toUnsignedInt(s[sStart]);
        switch (sp[0]) {
            case G0_ASCII:
                if (0xA1 <= s0 && s0 <= 0xDF)
                    return TranscodingInstruction.FUNso;
                return TranscodingInstruction.NOMAP;
            case G0_JISX0201_KATAKANA:
                c = s0 & 0x7F;
                if (0x21 <= c && c <= 0x5f)
                    return TranscodingInstruction.FUNso;
                break;
            case G0_JISX0208_1978:
                if ((0x21 <= s0 && s0 <= 0x28) || (0x30 <= s0 && s0 <= 0x74))
                    return iso2022jp_decoder_jisx0208_rest;
                break;
            case G0_JISX0208_1983:
                if ((0x21 <= s0 && s0 <= 0x28) ||
                        s0 == 0x2D ||
                        (0x30 <= s0 && s0 <= 0x74) ||
                        (0x79 <= s0 && s0 <= 0x7C)) {
                    /* 0x7F <= s0 && s0 <= 0x92) */
                    return iso2022jp_decoder_jisx0208_rest;
                }
                break;
        }
        return TranscodingInstruction.INVALID;
    }

    public static int funSoCp50221Decoder(byte[] statep, byte[] s, int sStart, int l, byte[] o, int oStart, int oSize) {
        int s0 = s[sStart]&0xFF;
        int s1;
        byte[] sp = statep;
        switch (s0) {
            case 0x1b:
                s1 = s[sStart+1]&0xFF;
                if (s1 == '(') {
                    switch ((s[sStart+l-1]&0xFF)) {
                        case 'B':
                        case 'J':
                            sp[0] = G0_ASCII;
                            break;
                        case 'I':
                            sp[0] = G0_JISX0201_KATAKANA;
                            break;
                    }
                }
                else {
                    switch (s[sStart+l-1]&0xFF) {
                        case '@':
                            sp[0] = G0_JISX0208_1978;
                            break;
                        case 'B':
                            sp[0] = G0_JISX0208_1983;
                            break;
                    }
                }
                return 0;
            case 0x0E:
                sp[0] = G0_JISX0201_KATAKANA;
                return 0;
            case 0x0F:
                sp[0] = G0_ASCII;
                return 0;
            default:
                if (sp[0] == G0_JISX0201_KATAKANA ||
                    (0xA1 <= s0 && s0 <= 0xDF && sp[0] == G0_ASCII)) {
                o[oStart] = (byte)0x8E;
                o[oStart+1] = (byte)(s0 | 0x80);
            }
        /* else if (0x7F == s[0] && s[0] <= 0x88) { */
            /* User Defined Characters */
            /* o[n++] = s[0] | 0xE0; */
            /* o[n++] = s[1] | 0x80; */
        /* else if (0x89 <= s[0] && s[0] <= 0x92) { */
            /* User Defined Characters 2 */
            /* o[n++] = 0x8f; */
            /* o[n++] = s[0] + 0x6C; */
            /* o[n++] = s[1] | 0x80; */
        /* } */
            else {
            /* JIS X 0208 */
            /* NEC Special Characters */
            /* NEC-selected IBM extended Characters */
                s1 = s[sStart+1]&0xFF;
                o[oStart] = (byte)(s0 | 0x80);
                o[oStart+1] = (byte)(s1 | 0x80);
            }
            return 2;
        }
    }

    public static int iso2022jpKddiInit(byte[] statep) {
        statep[0] = G0_ASCII;
        return 0;
    }

    public static final int iso2022jp_kddi_decoder_jisx0208_rest = Transcoding.WORDINDEX2INFO(16);

    public static int funSiIso2022jpKddiDecoder(byte[] statep, byte[] s, int sStart, int l) {
        int s0 = toUnsignedInt(s[sStart]);
        byte[] sp = statep;
        if (sp[0] == G0_ASCII) {
            return TranscodingInstruction.NOMAP;
        } else if (0x21 <= s0 && s0 <= 0x7e) {
            return iso2022jp_kddi_decoder_jisx0208_rest;
        } else {
            return TranscodingInstruction.INVALID;
        }
    }

    public static int funSoIso2022jpKddiDecoder(byte[] statep, byte[] s, int sStart, int l, byte[] o, int oStart, int oSize) {
        int s0 = toUnsignedInt(s[sStart]);
        int s1 = toUnsignedInt(s[sStart+1]);
        byte[] sp = statep;
        if (s0 == 0x1b) {
            if (s1 == '(') {
                switch (toUnsignedInt(s[sStart+l-1])) {
                    case 'B': /* US-ASCII */
                    case 'J': /* JIS X 0201 Roman */
                        sp[0] = G0_ASCII;
                        break;
                }
            }
            else {
                switch (toUnsignedInt(s[sStart+l-1])) {
                    case '@':
                        sp[0] = G0_JISX0208_1978;
                        break;

                    case 'B':
                        sp[0] = G0_JISX0208_1983;
                        break;
                }
            }
            return 0;
        }
        else {
            if (sp[0] == G0_JISX0208_1978) {
                o[oStart] = (byte)EMACS_MULE_LEADING_CODE_JISX0208_1978;
            } else {
                o[oStart] = (byte)EMACS_MULE_LEADING_CODE_JISX0208_1983;
            }
            o[oStart+1] = (byte)(s0 | 0x80);
            o[oStart+2] = (byte)(s1 | 0x80);
            return 3;
        }
    }

    public static int funSoIso2022jpKddiEncoder(byte[] statep, byte[] s, int sStart, int l, byte[] o, int oStart, int oSize) {
        int s0 = toUnsignedInt(s[sStart]);
        int s1, s2;
        byte[] sp = statep;
        int output0 = oStart;
        int newstate;

        if (l == 1)
            newstate = G0_ASCII;
        else if (s0 == EMACS_MULE_LEADING_CODE_JISX0208_1978)
            newstate = G0_JISX0208_1978;
        else
            newstate = G0_JISX0208_1983;

        if (sp[0] != newstate) {
            o[oStart++] = 0x1b;
            switch (newstate) {
                case G0_ASCII:
                    o[oStart++] = '(';
                    o[oStart++] = 'B';
                    break;
                case G0_JISX0208_1978:
                    o[oStart++] = '$';
                    o[oStart++] = '@';
                    break;
                default:
                    o[oStart++] = '$';
                    o[oStart++] = 'B';
                    break;
            }
            sp[0] = (byte)newstate;
        }

        if (l == 1) {
            o[oStart++] = (byte)(s0 & 0x7f);
        }
        else {
            s1 = toUnsignedInt(s[sStart+1]);
            s2 = toUnsignedInt(s[sStart+2]);
            o[oStart++] = (byte)(s1 & 0x7f);
            o[oStart++] = (byte)(s2 & 0x7f);
        }

        return oStart - output0;

    }

    public static int finishIso2022jpKddiEncoder(byte[] statep, byte[] o, int oStart, int oSize) {
        byte[] sp = statep;
        int output0 = oStart;

        if (sp[0] == G0_ASCII) return 0;

        o[oStart++] = 0x1b;
        o[oStart++] = '(';
        o[oStart++] = 'B';
        sp[0] = G0_ASCII;

        return oStart - output0;
    }

    public static int iso2022jpKddiEncoderResetSequence_size(byte[] statep) {
        byte[] sp = statep;
        if (sp[0] != G0_ASCII) return 3;
        return 0;
    }

    public static int fromUtf8MacInit(byte[] state) {
        bufClear(state);
        return 0;
    }

    private static final int STATUS_BUF_SIZE = 16;
    private static final int TOTAL_BUF_SIZE = STATUS_BUF_SIZE + 4 * 2; // status buf plus two ints

    private static final int bufBytesize(byte[] p) {
        return (bufEnd(p) - bufBeg(p) + STATUS_BUF_SIZE) % STATUS_BUF_SIZE;
    }

    private static final byte bufAt(byte[] sp, int pos) {
        pos += bufBeg(sp);
        pos %= STATUS_BUF_SIZE;
        return sp[pos];
    }

    private static void bufClear(byte[] state) {
        assert state.length >= 24 : "UTF8-MAC state not large enough";

        Arrays.fill(state, (byte)0);
    }

    public static int funSoFromUtf8Mac(byte[] statep, byte[] s, int sStart, int l, byte[] o, int oStart, int oSize) {
        byte[] sp = statep;
        int n = 0;

        switch (l) {
            case 1:
                n = fromUtf8MacFinish(sp, o, oStart, oSize);
                break;
            case 4:
                n = fromUtf8MacFinish(sp, o, oStart, oSize);
                o[oStart+n++] = s[sStart++];
                o[oStart+n++] = s[sStart++];
                o[oStart+n++] = s[sStart++];
                o[oStart+n++] = s[sStart++];
                return n;
        }

        bufPush(sp, s, sStart, l);
        n += bufApply(sp, o, oStart);
        return n;
    }

    private static void bufPush(byte[] sp, byte[] p, int pStart, int l) {
        int pend = pStart + l;
        while (pStart < pend) {
            /* if (sp->beg == sp->end) */
            sp[bufEndPostInc(sp)] = p[pStart++];
            bufEnd(sp, bufEnd(sp) % STATUS_BUF_SIZE);
        }
    }

    private static final int from_utf8_mac_nfc2 = Transcoding.WORDINDEX2INFO(35578);

    private static int bufApply(byte[] sp, byte[] o, int oStart) {
        int n = 0;
        int next_info;
        byte[] buf = {0,0,0};
        if (bufBytesize(sp) < 3 || (bufBytesize(sp) == 3 && bufAt(sp, 0) >= 0xE0)) {
        /* char length is less than 2 */
            return 0;
        }
        next_info = getInfo(from_utf8_mac_nfc2, sp);
        switch (next_info & 0x1F) {
            case TranscodingInstruction.THREEbt:
            case TranscodingInstruction.TWObt:
                buf[n++] = Transcoding.getBT1(next_info);
                buf[n++] = Transcoding.getBT2(next_info);
                if (TranscodingInstruction.THREEbt == (next_info & 0x1F))
                    buf[n++] = Transcoding.getBT3(next_info);
                bufClear(sp);
                bufPush(sp, buf, 0, n);
                return 0;
            default:
                return bufOutputChar(sp, o, oStart);
        }
    }

    private static boolean bufEmpty(byte[] sp) {
        return bufBeg(sp) == bufEnd(sp);
    }

    private static byte bufShift(byte[] sp) {
        /* if (sp->beg == sp->end) */
        int c = sp[bufBegPostInc(sp)];
        bufBeg(sp, bufBeg(sp) % STATUS_BUF_SIZE);
        return (byte)c;
    }

    private static boolean utf8Trailbyte(byte c) {
        return (c & 0xC0) == 0x80;
    }

    private static int bufOutputChar(byte[] sp, byte[] o, int oStart) {
        int n = 0;
        while (!bufEmpty(sp)) {
            o[oStart+n++] = bufShift(sp);
            if (!utf8Trailbyte(sp[bufBeg(sp)])) break;
        }
        return n;
    }

    private static int getInfo(int nextInfo, byte[] sp) {
        int pos = 0;
        while (pos < bufBytesize(sp)) {
            int next_byte = toUnsignedInt(bufAt(sp, pos++));
            if (next_byte < UTF8MAC_BL_MIN_BYTE(nextInfo) || UTF8MAC_BL_MAX_BYTE(nextInfo) < next_byte)
                nextInfo = TranscodingInstruction.INVALID;
            else {
                nextInfo = UTF8MAC_BL_ACTION(nextInfo, (byte)next_byte);
            }
            if ((nextInfo & 3) == 0) continue;
            break;
        }
        return nextInfo;
    }

    public static int UTF8MAC_BL_MIN_BYTE(int nextInfo) {
        return toUnsignedInt(From_UTF8_MAC_Transcoder.INSTANCE.byteArray[BL_BASE(nextInfo)]);
    }

    public static int UTF8MAC_BL_MAX_BYTE(int nextInfo) {
        return toUnsignedInt(From_UTF8_MAC_Transcoder.INSTANCE.byteArray[BL_BASE(nextInfo) + 1]);
    }

    public static int UTF8MAC_BL_OFFSET(int nextInfo, int b) {
        return toUnsignedInt(From_UTF8_MAC_Transcoder.INSTANCE.byteArray[BL_BASE(nextInfo) + 2 + b - UTF8MAC_BL_MIN_BYTE(nextInfo)]);
    }

    public static int UTF8MAC_BL_ACTION(int nextInfo, byte b) {
        return From_UTF8_MAC_Transcoder.INSTANCE.intArray[BL_INFO(nextInfo) + UTF8MAC_BL_OFFSET(nextInfo, toUnsignedInt(b))];
    }

    private static int BL_BASE(int nextInfo) {
        return BYTE_ADDR(BYTE_LOOKUP_BASE(WORD_ADDR(nextInfo)));
    }

    private static int BL_INFO(int nextInfo) {
        return WORD_ADDR(BYTE_LOOKUP_INFO(WORD_ADDR(nextInfo)));
    }

    private static int BYTE_ADDR(int index) {
        return index;
    }

    private static int WORD_ADDR(int index) {
        return TranscodeTableSupport.INFO2WORDINDEX(index);
    }

    private static int BYTE_LOOKUP_BASE(int bl) {
        return From_UTF8_MAC_Transcoder.INSTANCE.intArray[bl];
    }

    private static int BYTE_LOOKUP_INFO(int bl) {
        return From_UTF8_MAC_Transcoder.INSTANCE.intArray[bl + 1];
    }

    private static int bufInt(byte[] statep, int base) {
        return (statep[base] << 24) | (statep[base+1] << 16) | (statep[base+2] << 8) | (statep[base+3]);
    }

    private static void bufInt(byte[] statep, int base, int val) {
        statep[base] = (byte) (val >>> 24);
        statep[base+1] = (byte) (val >>> 16);
        statep[base+2] = (byte) (val >>> 8);
        statep[base+3] = (byte) val;
    }

    private static int bufBeg(byte[] statep) {
        return bufInt(statep, 16);
    }

    private static int bufEnd(byte[] statep) {
        return bufInt(statep, 20);
    }

    private static void bufBeg(byte[] statep, int end) {
        bufInt(statep, 16, end);
    }

    private static void bufEnd(byte[] statep, int end) {
        bufInt(statep, 20, end);
    }

    private static int bufEndPostInc(byte[] statep) {
        int end = bufInt(statep, 20);
        bufInt(statep, 20, end + 1);
        return end;
    }

    private static int bufBegPostInc(byte[] statep) {
        int beg = bufInt(statep, 16);
        bufInt(statep, 16, beg + 1);
        return beg;
    }

    public static int fromUtf8MacFinish(byte[] statep, byte[] o, int oStart, int oSize) {
        return bufOutputAll(statep, o, oStart);
    }

    private static int bufOutputAll(byte[] sp, byte[] o, int oStart) {
        int n = 0;
        while (!bufEmpty(sp)) {
            o[oStart+n++] = bufShift(sp);
        }
        return n;
    }

    private static final int ESCAPE_END = 0;
    private static final int ESCAPE_NORMAL = 1;

    public static int escapeXmlAttrQuoteInit(byte[] statep) {
        statep[0] = ESCAPE_END;
        return 0;
    }

    public static int funSoEscapeXmlAttrQuote(byte[] statep, byte[] s, int sStart, int l, byte[] o, int oStart, int oSize) {
        byte[] sp = statep;
        int n = 0;
        if (sp[0] == ESCAPE_END) {
            sp[0] = ESCAPE_NORMAL;
            o[oStart+n++] = '"';
        }
        o[oStart+n++] = s[sStart];
        return n;
    }

    public static int escapeXmlAttrQuoteFinish(byte[] statep, byte[] o, int oStart, int oSize) {
        byte[] sp = statep;
        int n = 0;

        if (sp[0] == ESCAPE_END) {
            o[oStart+n++] = '"';
        }

        o[oStart+n++] = '"';
        sp[0] = ESCAPE_END;

        return n;
    }

    private static final int NEWLINE_NORMAL = 0;
    private static final int NEWLINE_JUST_AFTER_CR = 1;

    private static final int MET_LF = 0x01;
    private static final int MET_CRLF = 0x02;
    private static final int MET_CR = 0x04;

    private static byte NEWLINE_STATE(byte[] sp) {
        return sp[0];
    }

    private static void NEWLINE_STATE(byte[] sp, int b) {
        sp[0] = (byte)b;
    }

    private static void NEWLINE_NEWLINES_MET(byte[] sp, int b) {
        sp[1] = (byte)b;
    }

    private static void NEWLINE_NEWLINES_MET_or_mask(byte[] sp, int b) {
        sp[1] |= (byte)b;
    }

    public static int universalNewlineInit(byte[] statep) {
        byte[] sp = statep;
        NEWLINE_STATE(sp, NEWLINE_NORMAL);
        NEWLINE_NEWLINES_MET(sp, 0);

        return 0;
    }

    public static int funSoUniversalNewline(byte[] statep, byte[] s, int sStart, int l, byte[] o, int oStart, int oSize) {
        int s0 = toUnsignedInt(s[sStart]);
        byte[] sp = statep;
        int len;
        if (s0 == '\n') {
            if (NEWLINE_STATE(sp) == NEWLINE_NORMAL) {
                NEWLINE_NEWLINES_MET_or_mask(sp, MET_LF);
            }
            else { /* JUST_AFTER_CR */
                NEWLINE_NEWLINES_MET_or_mask(sp, MET_CRLF);
            }
            o[oStart] = '\n';
            len = 1;
            NEWLINE_STATE(sp, NEWLINE_NORMAL);
        }
        else {
            len = 0;
            if (NEWLINE_STATE(sp) == NEWLINE_JUST_AFTER_CR) {
                o[oStart] = '\n';
                len = 1;
                NEWLINE_NEWLINES_MET_or_mask(sp, MET_CR);
            }
            if (s0 == '\r') {
                NEWLINE_STATE(sp, NEWLINE_JUST_AFTER_CR);
            }
            else {
                o[oStart+len++] = (byte)s0;
                NEWLINE_STATE(sp, NEWLINE_NORMAL);
            }
        }

        return len;
    }

    public static int universalNewlineFinish(byte[] statep, byte[] o, int oStart, int oSize) {
        byte[] sp = statep;
        int len = 0;
        if (NEWLINE_STATE(sp) == NEWLINE_JUST_AFTER_CR) {
            o[oStart] = '\n';
            len = 1;
            NEWLINE_NEWLINES_MET_or_mask(sp, MET_CR);
        }
        sp[0] = NEWLINE_NORMAL;
        return len;
    }
}
