Project: Amoeba-for-Aladdin
/**
 * <pre> 
 *  This program is free software; you can redistribute it and/or modify it under the terms of  
 * the GNU General Public License as published by the Free Software Foundation; either version 3 of the License,  
 * or (at your option) any later version.  
 *  
 *  This program is distributed in the hope that it will be useful,  
 * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.   
 * See the GNU General Public License for more details.  
 *  You should have received a copy of the GNU General Public License along with this program;  
 * if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA. 
 * </pre> 
 */
package com.meidusa.amoeba.mysql.net.packet; 
 
import java.io.UnsupportedEncodingException; 
import java.nio.ByteBuffer; 
import java.sql.SQLException; 
 
import com.meidusa.amoeba.mysql.io.Constants; 
import com.meidusa.amoeba.mysql.io.MySqlPacketConstant; 
import com.meidusa.amoeba.mysql.util.MysqlStringUtil; 
import com.meidusa.amoeba.mysql.util.SingleByteCharsetConverter; 
import com.meidusa.amoeba.net.Connection; 
import com.meidusa.amoeba.net.DatabaseConnection; 
import com.meidusa.amoeba.net.packet.AbstractPacketBuffer; 
import com.meidusa.amoeba.util.StringUtil; 
 
/**
 * 猜测:可能是用于Aladdin Proxy Server到MySQL连接的数据流缓存类 
 * @author Li Hui 
 *  
 * 该类负责 发送、接收socket输入流,并且可以根据包头信息,构造出ByteBuffer 
 * @author <a href=mailto:[email protected]>Struct chen</a> 
 * @author hexianmao 
 */
 
public class MysqlPacketBuffer extends AbstractPacketBuffer { 
 
    static final int  MAX_BYTES_TO_DUMP = 512
 
    static final int  NO_LENGTH_LIMIT   = -1
 
    static final long NULL_LENGTH       = -1
 
    protected boolean wasMultiPacket    = false
 
    public static int getPacketLength(byte[] byteBuffer) { 
        if (byteBuffer == null || byteBuffer.length < 4) { 
            return 0
        } else { 
            int packetLength = (byteBuffer[0] & 0xff) + ((byteBuffer[1] & 0xff) << 8) + ((byteBuffer[2] & 0xff) << 16); 
            return packetLength; 
        } 
    } 
    /**
     *  
     * @param buf 
     * @author Li Hui 
     */
 
    public MysqlPacketBuffer(byte[] buf){ 
        super(buf); 
        position = MySqlPacketConstant.HEADER_SIZE; 
    } 
 
    public MysqlPacketBuffer(int size){ 
        super(size); 
        position = MySqlPacketConstant.HEADER_SIZE; 
    } 
 
    public void init(Connection conn) { 
        super.init(conn); 
    } 
 
    public DatabaseConnection getConnection() { 
        return (DatabaseConnection) this.conn; 
    } 
 
    public int getBufLength() { 
        return length; 
    } 
 
    public void setBufLength(int length) { 
        this.length = length; 
    } 
 
    public int getPacketLength() { 
        if (buffer == null || buffer.length < 4) { 
            return 0
        } else { 
            int packetLength = (buffer[0] & 0xff) + ((buffer[1] & 0xff) << 8) + ((buffer[2] & 0xff) << 16); 
            return packetLength; 
        } 
    } 
 
    final void clear() { 
        position = MySqlPacketConstant.HEADER_SIZE; 
    } 
 
    final void dump() { 
        dump(length); 
    } 
 
    final String dump(int numBytes) { 
        return StringUtil.dumpAsHex(getBytes(0, numBytes > length ? length : numBytes), numBytes > length ? length : numBytes); 
    } 
 
    final String dumpClampedBytes(int numBytes) { 
        int numBytesToDump = numBytes < MAX_BYTES_TO_DUMP ? numBytes : MAX_BYTES_TO_DUMP; 
 
        String dumped = StringUtil.dumpAsHex(getBytes(0, numBytesToDump > length ? length : numBytesToDump), numBytesToDump > length ? length : numBytesToDump); 
 
        if (numBytesToDump < numBytes) { 
            return dumped + " ....(packet exceeds max. dump length)"
        } 
 
        return dumped; 
    } 
 
    final void dumpHeader() { 
        for (int i = 0; i < MySqlPacketConstant.HEADER_SIZE; i++) { 
            String hexVal = Integer.toHexString(readByte(i) & 0xff); 
 
            if (hexVal.length() == 1) { 
                hexVal = "0" + hexVal; //$NON-NLS-1$ 
            } 
 
            System.out.print(hexVal + " "); //$NON-NLS-1$ 
        } 
    } 
 
    final void dumpNBytes(int start, int nBytes) { 
        StringBuffer asciiBuf = new StringBuffer(); 
 
        for (int i = start; (i < (start + nBytes)) && (i < length); i++) { 
            String hexVal = Integer.toHexString(readByte(i) & 0xff); 
 
            if (hexVal.length() == 1) { 
                hexVal = "0" + hexVal; //$NON-NLS-1$ 
            } 
 
            System.out.print(hexVal + " "); //$NON-NLS-1$ 
 
            if ((readByte(i) > 32) && (readByte(i) < 127)) { 
                asciiBuf.append((char) readByte(i)); 
            } else { 
                asciiBuf.append("."); //$NON-NLS-1$ 
            } 
 
            asciiBuf.append(" "); //$NON-NLS-1$ 
        } 
 
        System.out.println("    " + asciiBuf.toString()); //$NON-NLS-1$ 
    } 
 
    protected void ensureCapacity(int additionalData) { 
        if ((position + additionalData) > length) { 
            if (buffer.length <= (position + additionalData)) { 
                int newLength = position + (int) (additionalData * 1.5) + 1
 
                while (newLength < (position + additionalData + 1)) { 
                    newLength = (int) (newLength * 1.5); 
                } 
 
                byte[] newBytes = new byte[newLength]; 
                System.arraycopy(buffer, 0, newBytes, 0this.buffer.length); 
                buffer = newBytes; 
            } 
            length = buffer.length; 
        } 
    } 
 
    /**
     * Skip over a length-encoded string 
     *  
     * @return The position past the end of the string 
     */
 
    public int fastSkipLenString() { 
        long len = this.readFieldLength(); 
        position += len; 
        return (int) len; 
    } 
 
    public void fastSkipLenByteArray() { 
        long len = this.readFieldLength(); 
        if (len == NULL_LENGTH || len == 0) { 
            return
        } 
        position += len; 
    } 
 
    protected final byte[] getBufferSource() { 
        return buffer; 
    } 
 
    /**
     * Returns the array of bytes this Buffer is using to read from. 
     *  
     * @return byte array being read from 
     */
 
    public byte[] getByteBuffer() { 
        return buffer; 
    } 
 
    public final byte[] getBytes(int len) { 
        byte[] b = new byte[len]; 
        System.arraycopy(this.buffer, this.position, b, 0, len); 
        this.position += len; // update cursor 
        return b; 
    } 
 
    public byte[] getBytes(int offset, int len) { 
        byte[] dest = new byte[len]; 
        System.arraycopy(this.buffer, offset, dest, 0, len); 
        return dest; 
    } 
 
    int getCapacity() { 
        return this.buffer.length; 
    } 
 
    final boolean isLastDataPacket() { 
        return ((length < 9) && ((this.buffer[4] & 0xff) == 254)); 
    } 
 
    final long newReadLength() { 
        int sw = this.buffer[this.position++] & 0xff
        switch (sw) { 
            case 251
                return 0
            case 252
                return readInt(); 
            case 253
                return readLongInt(); 
            case 254// changed for 64 bit lengths 
                return readLongLong(); 
            default
                return sw; 
        } 
    } 
 
    final long readFieldLength() { 
        int i = this.buffer[this.position++] & 0xff
        switch (i) { 
            case 251
                return NULL_LENGTH; 
            case 252
                return readInt(); 
            case 253
                return readLongInt(); 
            case 254
                return readLongLong(); 
            default
                return i; 
        } 
    } 
 
    final int readInt() { 
        byte[] b = this.buffer; // a little bit optimization 
        return (b[this.position++] & 0xff) | ((b[this.position++] & 0xff) << 8); 
    } 
 
    final int readIntAsLong() { 
        byte[] b = this.buffer; 
        return (b[this.position++] & 0xff) | ((b[this.position++] & 0xff) << 8) | ((b[this.position++] & 0xff) << 16) | ((b[this.position++] & 0xff) << 24); 
    } 
 
    final byte[] readLenByteArray(int offset) { 
        long len = this.readFieldLength(); 
        if (len == NULL_LENGTH) { 
            return null
        } 
        if (len == 0) { 
            return Constants.EMPTY_BYTE_ARRAY; 
        } 
        this.position += offset; 
        return getBytes((int) len); 
    } 
 
    final long readLength() { 
        int sw = this.buffer[this.position++] & 0xff
        switch (sw) { 
            case 251
                return 0
            case 252
                return readInt(); 
            case 253
                return readLongInt(); 
            case 254
                return readLong(); 
            default
                return sw; 
        } 
    } 
 
    final long readLong() { 
        byte[] b = this.buffer; 
        return ((long) b[this.position++] & 0xff) | (((long) b[this.position++] & 0xff) << 8) | ((long) (b[this.position++] & 0xff) << 16) | ((long) (b[this.position++] & 0xff) << 24); 
    } 
 
    final int readLongInt() { 
        byte[] b = this.buffer; 
        return (b[this.position++] & 0xff) | ((b[this.position++] & 0xff) << 8) | ((b[this.position++] & 0xff) << 16); 
    } 
 
    final long readLongLong() { 
        byte[] b = this.buffer; 
        return (b[this.position++] & 0xff) | ((long) (b[this.position++] & 0xff) << 8) | ((long) (b[this.position++] & 0xff) << 16) | ((long) (b[this.position++] & 0xff) << 24) | ((long) (b[this.position++] & 0xff) << 32) | ((long) (b[this.position++] & 0xff) << 40) | ((long) (b[this.position++] & 0xff) << 48) | ((long) (b[this.position++] & 0xff) << 56); 
    } 
 
    final int readnBytes() { 
        int sw = this.buffer[this.position++] & 0xff
        switch (sw) { 
            case 1
                return this.buffer[this.position++] & 0xff
            case 2
                return this.readInt(); 
            case 3
                return this.readLongInt(); 
            case 4
                return (intthis.readLong(); 
            default
                return 255
        } 
    } 
 
    final String readString() { 
        int i = position; 
        int len = 0
        int maxLen = length; 
 
        while ((i < maxLen) && (buffer[i] != 0)) { 
            len++; 
            i++; 
        } 
 
        String s = new String(buffer, position, len); 
        position += (len + 1); // update cursor 
 
        return s; 
    } 
 
    final String readLengthCodedString(String encoding) { 
        int fieldLength = (int) readFieldLength(); 
        if (fieldLength == 0) { 
            return null
        } 
        try { 
            if (encoding != null) { 
                return new String(buffer, position, fieldLength, encoding); 
            } else { 
                return new String(buffer, position, fieldLength); 
            } 
        } catch (UnsupportedEncodingException e) { 
            // TODO logger exception 
            return new String(buffer, position, fieldLength); 
        } finally { 
            position += fieldLength; // update cursor 
        } 
    } 
 
    public static boolean isErrorPacket(byte[] bty) { 
        return isPacketType(bty, (byte0xff); 
    } 
 
    public static boolean isEofPacket(byte[] bty) { 
        return isPacketType(bty, (byte0xfe); 
    } 
 
    public static int increasePacketId(int packetId) { 
        if (packetId >= 255) { 
            return 0
        } else { 
            return packetId++; 
        } 
    } 
 
    public static boolean isPacketType(byte[] bytes, byte type) { 
        if (bytes.length >= 5) { 
            return bytes[4] == type; 
        } 
        return false
    } 
 
    public static boolean isOkPacket(byte[] bty) { 
        return isPacketType(bty, (byte0x00); 
    } 
 
    final String readString(String encoding) { 
        int i = position; 
        int len = 0
        int maxLen = length; 
 
        while ((i < maxLen) && (buffer[i] != 0)) { 
            len++; 
            i++; 
        } 
 
        try { 
            return new String(buffer, position, len, encoding); 
        } catch (UnsupportedEncodingException e) { 
            // TODO logger exception 
            return new String(buffer, position, len).trim(); 
        } finally { 
            position += (len + 1); // update cursor 
        } 
    } 
 
    /**
     * Sets the array of bytes to use as a buffer to read from. 
     *  
     * @param buffer the array of bytes to use as a buffer 
     */
 
    public void setByteBuffer(byte[] byteBufferToSet) { 
        this.buffer = byteBufferToSet; 
    } 
 
    /**
     * Sets whether this packet was part of a multipacket 
     *  
     * @param flag was this packet part of a multipacket? 
     */
 
    public void setWasMultiPacket(boolean flag) { 
        this.wasMultiPacket = flag; 
    } 
 
    public String toString() { 
        return dumpClampedBytes(getPosition()); 
    } 
 
    public String toSuperString() { 
        return super.toString(); 
    } 
 
    /**
     * Was this packet part of a multipacket? 
     *  
     * @return was this packet part of a multipacket? 
     */
 
    public boolean wasMultiPacket() { 
        return this.wasMultiPacket; 
    } 
 
    // Write a byte array 
    final void writeBytesNoNull(byte[] bytes) { 
        int len = bytes.length; 
        ensureCapacity(len); 
        System.arraycopy(bytes, 0this.buffer, this.position, len); 
        this.position += len; 
    } 
 
    // Write a byte array with the given offset and length 
    final void writeBytesNoNull(byte[] bytes, int offset, int length) throws SQLException { 
        ensureCapacity(length); 
        System.arraycopy(bytes, offset, this.buffer, this.position, length); 
        this.position += length; 
    } 
 
    public final void writeDouble(double d) { 
        long l = Double.doubleToLongBits(d); 
        writeLongLong(l); 
    } 
 
    public double readDouble() { 
        long result = readLongLong(); 
        return Double.longBitsToDouble(result); 
    } 
 
    public final void writeFieldLength(long length) { 
        if (length < 251) { 
            writeByte((byte) length); 
        } else if (length < 65536L) { 
            ensureCapacity(3); 
            writeByte((byte252); 
            writeInt((int) length); 
        } else if (length < 16777216L) { 
            ensureCapacity(4); 
            writeByte((byte253); 
            writeLongInt((int) length); 
        } else { 
            ensureCapacity(9); 
            writeByte((byte254); 
            writeLongLong(length); 
        } 
    } 
 
    public final void writeFloat(float f) { 
        ensureCapacity(4); 
        int i = Float.floatToIntBits(f); 
        byte[] b = this.buffer; 
        b[this.position++] = (byte) (i & 0xff); 
        b[this.position++] = (byte) (i >>> 8); 
        b[this.position++] = (byte) (i >>> 16); 
        b[this.position++] = (byte) (i >>> 24); 
    } 
 
    final float readFloat() { 
        byte[] b = this.buffer; 
        int result = ((int) b[this.position++] & 0xff) | (((int) b[this.position++] & 0xff) << 8) | ((int) (b[this.position++] & 0xff) << 16) | ((int) (b[this.position++] & 0xff) << 24); 
        return Float.intBitsToFloat(result); 
    } 
 
    // 
    final void writeInt(int i) { 
        ensureCapacity(2); 
        byte[] b = this.buffer; 
        b[this.position++] = (byte) (i & 0xff); 
        b[this.position++] = (byte) (i >>> 8); 
    } 
 
    // Write a String using the specified character encoding 
    final void writeLenBytes(byte[] b) { 
        int len = b.length; 
        ensureCapacity(len + 9); 
        writeFieldLength(len); 
        System.arraycopy(b, 0this.buffer, this.position, len); 
        this.position += len; 
    } 
 
    public final void writeLengthCodedString(String s, String encoding) { 
        if (s != null) { 
            byte[] b; 
            try { 
                b = s.getBytes(encoding); 
            } catch (UnsupportedEncodingException e) { 
                // TODO 
                e.printStackTrace(); 
                b = s.getBytes(); 
            } 
            ensureCapacity(b.length + 9); 
            this.writeFieldLength(b.length); 
            this.writeBytesNoNull(b); 
        } else { 
            this.writeByte((byte0); 
        } 
    } 
 
    // Write a String using the specified character encoding 
    final void writeLenString(String s, String encoding, String serverEncoding, SingleByteCharsetConverter converter, 
                              boolean parserKnowsUnicode) throws UnsupportedEncodingException { 
        byte[] b = null
 
        if (converter != null) { 
            b = converter.toBytes(s); 
        } else { 
            b = MysqlStringUtil.getBytes(s, converter, encoding, serverEncoding, parserKnowsUnicode); 
        } 
 
        int len = b.length; 
        ensureCapacity(len + 9); 
        writeFieldLength(len); 
        System.arraycopy(b, 0this.buffer, this.position, len); 
        this.position += len; 
    } 
 
    final void writeLong(long i) { 
        ensureCapacity(4); 
 
        byte[] b = this.buffer; 
        b[this.position++] = (byte) (i & 0xff); 
        b[this.position++] = (byte) (i >>> 8); 
        b[this.position++] = (byte) (i >>> 16); 
        b[this.position++] = (byte) (i >>> 24); 
    } 
 
    final void writeLongInt(int i) { 
        ensureCapacity(3); 
        byte[] b = this.buffer; 
        b[this.position++] = (byte) (i & 0xff); 
        b[this.position++] = (byte) (i >>> 8); 
        b[this.position++] = (byte) (i >>> 16); 
    } 
 
    final void writeLongLong(long i) { 
        ensureCapacity(8); 
        byte[] b = this.buffer; 
        b[this.position++] = (byte) (i & 0xff); 
        b[this.position++] = (byte) (i >>> 8); 
        b[this.position++] = (byte) (i >>> 16); 
        b[this.position++] = (byte) (i >>> 24); 
        b[this.position++] = (byte) (i >>> 32); 
        b[this.position++] = (byte) (i >>> 40); 
        b[this.position++] = (byte) (i >>> 48); 
        b[this.position++] = (byte) (i >>> 56); 
    } 
 
    // Write null-terminated string 
    final void writeString(String s) { 
        ensureCapacity((s.length() * 2) + 1); 
        writeStringNoNull(s); 
        this.buffer[this.position++] = 0
    } 
 
    // Write null-terminated string in the given encoding 
    final void writeString(String s, String encoding) throws UnsupportedEncodingException { 
        ensureCapacity((s.length() * 2) + 1); 
        writeStringNoNull(s, encoding, encoding, false); 
        this.buffer[this.position++] = 0
    } 
 
    // Write string, with no termination 
    final void writeStringNoNull(String s) { 
        int len = s.length(); 
        ensureCapacity(len * 2); 
        System.arraycopy(s.getBytes(), 0this.buffer, this.position, len); 
        this.position += len; 
    } 
 
    // Write a String using the specified character encoding 
    final void writeStringNoNull(String s, String encoding, String serverEncoding, boolean parserKnowsUnicode) 
                                                                                                              throws UnsupportedEncodingException { 
        byte[] b = null
        SingleByteCharsetConverter converter = SingleByteCharsetConverter.getInstance(encoding); 
        b = MysqlStringUtil.getBytes(s, converter, encoding, serverEncoding, parserKnowsUnicode); 
 
        int len = b.length; 
        ensureCapacity(len); 
        System.arraycopy(b, 0this.buffer, this.position, len); 
        this.position += len; 
    } 
 
    // 将从0当到前位置的所有字节写入到 ByteBuffer中,并且将 ByteBuffer position设置到0 
    public ByteBuffer toByteBuffer() { 
        ByteBuffer buffer = ByteBuffer.allocate(this.getPacketLength() + 4); 
        buffer.put(this.buffer, 0this.getPacketLength() + 4); 
        buffer.rewind(); 
        return buffer; 
    } 
 
}