/*

   Derby - Class org.apache.derby.client.net.Reply

   Licensed to the Apache Software Foundation (ASF) under one or more
   contributor license agreements.  See the NOTICE file distributed with
   this work for additional information regarding copyright ownership.
   The ASF licenses this file to You under the Apache License, Version 2.0
   (the "License"); you may not use this file except in compliance with
   the License.  You may obtain a copy of the License at

      http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.

*/

package org.apache.derby.client.net;


import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.Arrays;
import org.apache.derby.client.am.Agent;

import org.apache.derby.client.am.SignedBinary;
import org.apache.derby.client.am.SqlException;
import org.apache.derby.client.am.DisconnectException;
import org.apache.derby.client.am.ClientMessageId;

import org.apache.derby.shared.common.reference.SQLState;
import org.apache.derby.shared.common.reference.MessageId;

class Reply {
    protected Agent agent_;
    protected NetAgent netAgent_; //cheat-link to (NetAgent) agent_

    protected final static int DEFAULT_BUFFER_SIZE = 32767;
    protected byte[] buffer_;
    protected int pos_;
    protected int count_;

    private int topDdmCollectionStack_;
    private final static int MAX_MARKS_NESTING = 10;
    private int[] ddmCollectionLenStack_;
    private int ddmScalarLen_; // a value of -1 -> streamed ddm -> length unknown
    private final static int EMPTY_STACK = -1;

    protected boolean ensuredLengthForDecryption_ = false; // A layer lengths have already been ensured in decrypt method.
    protected byte[] longBufferForDecryption_ = null;
    protected int longPosForDecryption_ = 0;
    protected byte[] longValueForDecryption_ = null;
    protected int longCountForDecryption_ = 0;

    protected int dssLength_;
    private boolean dssIsContinued_;
    private boolean dssIsChainedWithSameID_;
    private int dssCorrelationID_;

    protected int peekedLength_ = 0;
    private int peekedCodePoint_ = END_OF_COLLECTION; // saves the peeked codept
    private int peekedNumOfExtendedLenBytes_ = 0;
    private int currentPos_ = 0;

    final static int END_OF_COLLECTION = -1;
    final static int END_OF_SAME_ID_CHAIN = -2;

    Reply(NetAgent netAgent, int bufferSize) {
        buffer_ = new byte[bufferSize];
        agent_ = netAgent_ = netAgent;
        ddmCollectionLenStack_ = new int[Reply.MAX_MARKS_NESTING];
        initialize();
    }

    final void initialize() {
        pos_ = 0;
        count_ = 0;
        topDdmCollectionStack_ = Reply.EMPTY_STACK;
        Arrays.fill(ddmCollectionLenStack_, 0);
        ddmScalarLen_ = 0;
        dssLength_ = 0;
        dssIsContinued_ = false;
        dssIsChainedWithSameID_ = false;
        dssCorrelationID_ = 1;
    }

    final int getDdmLength() {
        return ddmScalarLen_;
    }

    // This is a helper method which shifts the buffered bytes from
    // wherever they are in the current buffer to the beginning of
    // different buffer (note these buffers could be the same).
    // State information is updated as needed after the shift.
    private void shiftBuffer(byte[] destinationBuffer) {
        // calculate the size of the data in the current buffer.
        int sz = count_ - pos_;

        // copy this data to the new buffer starting at position 0.
        System.arraycopy(buffer_, pos_, destinationBuffer, 0, sz);

        // update the state information for data in the new buffer.
        pos_ = 0;
        count_ = sz;

        // replace the old buffer with the new buffer.
        buffer_ = destinationBuffer;
    }

    // This method makes sure there is enough room in the buffer
    // for a certain number of bytes.  This method will allocate
    // a new buffer if needed and shift the bytes in the current buffer
    // to make ensure space is available for a fill.  Right now
    // this method will shift bytes as needed to make sure there is
    // as much room as possible in the buffer before trying to
    // do the read.  The idea is to try to have space to get as much data as possible
    // if we need to do a read on the socket's stream.
    private void ensureSpaceInBufferForFill(int desiredSpace) {
        // calculate the total unused space in the buffer.
        // this includes any space at the end of the buffer and any free
        // space at the beginning resulting from bytes already read.
        int currentAvailableSpace = (buffer_.length - count_) + pos_;

        // check to see if there is enough free space.
        if (currentAvailableSpace < desiredSpace) {

            // there is not enough free space so we need more storage.
            // we are going to double the buffer unless that happens to still be too small.
            // if more than double the buffer is needed, use the smallest amount over this as possible.
            int doubleBufferSize = (2 * buffer_.length);

            int minumNewBufferSize = (desiredSpace - currentAvailableSpace) + buffer_.length;
            int newsz = minumNewBufferSize <= doubleBufferSize ? doubleBufferSize : minumNewBufferSize;

            byte[] newBuffer = new byte[newsz];

            // shift everything from the old buffer to the new buffer
            shiftBuffer(newBuffer);
        } else {

            // there is enough free space in the buffer but let's make sure it is all at the end.
            // this is also important because if we are going to do a read, it would be nice
            // to get as much data as possible and making room at the end if the buffer helps to
            // ensure this.
            if (pos_ != 0) {
                shiftBuffer(buffer_);
            }
        }
    }

    // This method will attempt to read a minimum number of bytes
    // from the underlying stream.  This method will keep trying to
    // read bytes until it has obtained at least the minimum number.
    // Now returns the total bytes read for decryption, use to return void.
    private int fill(int minimumBytesNeeded) throws DisconnectException {
        // make sure that there is enough space in the buffer to hold
        // the minimum number of bytes needed.
        ensureSpaceInBufferForFill(minimumBytesNeeded);

        // read until the minimum number of bytes needed is now in the buffer.
        // hopefully the read method will return as many bytes as it can.
        int totalBytesRead = 0;
        int actualBytesRead = 0;
        do {
            try {
                // oops, we shouldn't expose the agent's input stream here, collapse this into a read method on the agent
                actualBytesRead = netAgent_.getInputStream().read(buffer_, count_, buffer_.length - count_);
            } catch (IOException ioe) {
                netAgent_.throwCommunicationsFailure(ioe);
            } finally {
                if (agent_.loggingEnabled()) {
                    ((NetLogWriter) netAgent_.logWriter_).traceProtocolFlow(buffer_,
                            count_,
                            actualBytesRead,
                            NetLogWriter.TYPE_TRACE_RECEIVE,
                            "Reply",
                            "fill",
                            2); // tracepoint
                }
            }
            // DERBY-2747: only count if we actually read something
            if (actualBytesRead > 0) {
                count_ += actualBytesRead;
                totalBytesRead += actualBytesRead;
            }

        } while ((totalBytesRead < minimumBytesNeeded) && (actualBytesRead != -1));

        if (actualBytesRead == -1) {
            if (totalBytesRead < minimumBytesNeeded) {
                netAgent_.accumulateChainBreakingReadExceptionAndThrow(
                    new DisconnectException(netAgent_,
                        new ClientMessageId(SQLState.NET_INSUFFICIENT_DATA),
                        minimumBytesNeeded, totalBytesRead));
            }
        }
        return totalBytesRead;
    }

    // Make sure a certain amount of Layer A data is in the buffer.
    // The data will be in the buffer after this method is called.
    // Now returns the total bytes read for decryption, use to return void.
    private int ensureALayerDataInBuffer(int desiredDataSize)
            throws DisconnectException {
        int totalBytesRead = 0;
        // calulate the the number of bytes in the buffer.
        int avail = count_ - pos_;

        // read more bytes off the network if the data is not in the buffer already.
        if (avail < desiredDataSize) {
            totalBytesRead = fill(desiredDataSize - avail);
        }
        return totalBytesRead;
    }

    protected final void ensureBLayerDataInBuffer(int desiredDataSize) throws DisconnectException {
        if (dssIsContinued_ && (desiredDataSize > dssLength_)) {
            int continueDssHeaderCount =
                    (((desiredDataSize - dssLength_) / 32767) + 1);
            ensureALayerDataInBuffer(desiredDataSize + (continueDssHeaderCount * 2));
            compressBLayerData(continueDssHeaderCount);
            return;
        }
        ensureALayerDataInBuffer(desiredDataSize);
    }

    // this will probably never be called.
    // it is included here in the highly unlikely event that a reply object
    // exceeds 32K.  for opimization purposes, we should consider
    // removing this.  removing this should be ok since we handle most
    // big stuff returned from the server (qrydta's for example) by
    // copying out the data into some other storage.  any extended dss header
    // info will be removed in the copying process.
    private void compressBLayerData(int continueDssHeaderCount)
            throws DisconnectException {
        int tempPos = 0;

        // jump to the last continuation header.
        for (int i = 0; i < continueDssHeaderCount; i++) {
            // the first may be less than the size of a full dss
            if (i == 0) {
                // only jump by the number of bytes remaining in the current dss
                tempPos = pos_ + dssLength_;
            } else {
                // all other jumps are for a full continued dss
                tempPos += 32767;
            }
        }

        // for each of the dss headers to remove,
        // read out the continuation header and increment the dss length by the
        // size of the conitnation bytes,  then shift the continuation data as needed.
        int shiftSize = 0;
        int bytesToShift;
        int continueHeaderLength;
        int newDssLength = 0;
        for (int i = 0; i < continueDssHeaderCount; i++) {

            continueHeaderLength = ((buffer_[tempPos] & 0xFF) << 8) +
                    ((buffer_[tempPos + 1] & 0xFF) << 0);

            if (i == 0) {
                // if this is the last one (farthest down stream and first to strip out)

                if ((continueHeaderLength & 0x8000) == 0x8000) {
                    // the last dss header is again continued
                    continueHeaderLength = 32767;
                    dssIsContinued_ = true;
                } else {
                    // the last dss header was not contiued so update continue state flag
                    dssIsContinued_ = false;
                }
                // the very first shift size is 2
                shiftSize = 2;
            } else {
                // already removed the last header so make sure the chaining flag is on
                if ((continueHeaderLength & 0x8000) == 0x8000) {
                    continueHeaderLength = 32767;
                } else {
                    // this is a syntax error but not really certain which one.
                    // for now pick 0x02 which is Dss header Length does not match the number
                    // of bytes of data found.
                    doSyntaxrmSemantics(CodePoint.SYNERRCD_DSS_LENGTH_BYTE_NUMBER_MISMATCH);
                }
                // increase the shift size by 2
                shiftSize += 2;
            }

            // it is a syntax error if the dss continuation is less than or equal to two
            if (continueHeaderLength <= 2) {
                doSyntaxrmSemantics(CodePoint.SYNERRCD_DSS_CONT_LESS_OR_EQUAL_2);
            }

            newDssLength += (continueHeaderLength - 2);

            // calculate the number of bytes to shift
            if (i != (continueDssHeaderCount - 1)) {
                bytesToShift = 32767;
            } else {
                bytesToShift = dssLength_;
            }

            tempPos -= (bytesToShift - 2);
            System.arraycopy(buffer_, tempPos - shiftSize, buffer_, tempPos , bytesToShift);
        }
        // reposition the start of the data after the final dss shift.
        pos_ = tempPos;
        dssLength_ = dssLength_ + newDssLength;
    }

    private void readDssHeader() throws DisconnectException {
        int correlationID;
        int nextCorrelationID;
        ensureALayerDataInBuffer(6);

        // read out the dss length
        dssLength_ =
                ((buffer_[pos_++] & 0xFF) << 8) +
                ((buffer_[pos_++] & 0xFF) << 0);

        // Remember the old dss length for decryption only.
        int oldDssLength = dssLength_;

        // check for the continuation bit and update length as needed.
        if ((dssLength_ & 0x8000) == 0x8000) {
            dssLength_ = 32767;
            dssIsContinued_ = true;
        } else {
            dssIsContinued_ = false;
        }

        if (dssLength_ < 6) {
            doSyntaxrmSemantics(CodePoint.SYNERRCD_DSS_LESS_THAN_6);
        }

        // If the GDS id is not valid, or
        // if the reply is not an RPYDSS nor
        // a OBJDSS, then throw an exception.
        if ((buffer_[pos_++] & 0xFF) != 0xd0) {
            doSyntaxrmSemantics(CodePoint.SYNERRCD_CBYTE_NOT_D0);
        }

        int gdsFormatter = buffer_[pos_++] & 0xFF;
        if (((gdsFormatter & 0x02) != 0x02)
                && ((gdsFormatter & 0x03) != 0x03)
                && ((gdsFormatter & 0x04) != 0x04)) {
            doSyntaxrmSemantics(CodePoint.SYNERRCD_FBYTE_NOT_SUPPORTED);
        }

        // Determine if the current DSS is chained with the
        // next DSS, with the same or different request ID.
        if ((gdsFormatter & 0x40) == 0x40) {    // on indicates structure chained to next structure
            if ((gdsFormatter & 0x10) == 0x10) {
                dssIsChainedWithSameID_ = true;
                nextCorrelationID = dssCorrelationID_;
            } else {
                dssIsChainedWithSameID_ = false;
                nextCorrelationID = dssCorrelationID_ + 1;
            }
        } else {
            // chaining bit not b'1', make sure DSSFMT bit3 not b'1'
            if ((gdsFormatter & 0x10) == 0x10) {  // Next DSS can not have same correlator
                doSyntaxrmSemantics(CodePoint.SYNERRCD_CHAIN_OFF_SAME_NEXT_CORRELATOR);
            }

            // chaining bit not b'1', make sure no error continuation
            if ((gdsFormatter & 0x20) == 0x20) { // must be 'do not continue on error'
                doSyntaxrmSemantics(CodePoint.SYNERRCD_CHAIN_OFF_ERROR_CONTINUE);
            }

            dssIsChainedWithSameID_ = false;
            nextCorrelationID = 1;
        }

        correlationID =
                ((buffer_[pos_++] & 0xFF) << 8) +
                ((buffer_[pos_++] & 0xFF) << 0);

        // corrid must be the one expected or a -1 which gets returned in some error cases.
        if ((correlationID != dssCorrelationID_) && (correlationID != 0xFFFF)) {
            doSyntaxrmSemantics(CodePoint.SYNERRCD_INVALID_CORRELATOR);
        } else {
            dssCorrelationID_ = nextCorrelationID;
        }
        dssLength_ -= 6;
        if ((gdsFormatter & 0x04) == 0x04) {
            decryptData(gdsFormatter, oldDssLength);  //we have to decrypt data here because
        }
        //we need the decrypted codepoint. If
        //Data is very long > 32767, we have to
        //get all the data first because decrypt
        //piece by piece doesn't work.
    }


    private void decryptData(int gdsFormatter, int oldDssLength)
            throws DisconnectException {
        boolean readHeader;

        if (dssLength_ == 32761) {
            ByteArrayOutputStream baos;
            int copySize = 0;

            baos = new ByteArrayOutputStream();

            // set the amount to read for the first segment
            copySize = dssLength_; // note: has already been adjusted for headers

            do {
                // determine if a continuation header needs to be read after the data
                if (dssIsContinued_) {
                    readHeader = true;
                } else {
                    readHeader = false;
                }

                // read the segment
                ensureALayerDataInBuffer(copySize);
                adjustLengths(copySize);
                baos.write(buffer_, pos_, copySize);
                pos_ += copySize;

                // read the continuation header, if necessary
                if (readHeader) {
                    readDSSContinuationHeader();
                }

                copySize = dssLength_;
            } while (readHeader == true);
            byte[] cipherBytes = baos.toByteArray();
            byte[] clearedByte = null;
            try {
                clearedByte = netAgent_.netConnection_.getEncryptionManager().decryptData(cipherBytes,
                        NetConfiguration.SECMEC_EUSRIDPWD,
                        netAgent_.netConnection_.getTargetPublicKey(),
                        netAgent_.netConnection_.getTargetPublicKey());
            } catch (SqlException e) {
                //throw new SqlException (agent_.logWriter_, "error in decrypting data");
            }

            //The decrypted data is for one codepoint only. We need to save the data follows this codepoint
            longBufferForDecryption_ = new byte[buffer_.length - pos_];
            longPosForDecryption_ = 0;
            count_ = count_ - pos_;
            longCountForDecryption_ = count_;
            System.arraycopy(buffer_, pos_, longBufferForDecryption_, 0, buffer_.length - pos_);

            //copy the clear data to buffer_
            if (clearedByte.length >= 32767) {
                System.arraycopy(clearedByte, 0, buffer_, 0, 32767);
            } else {
                System.arraycopy(clearedByte, 0, buffer_, 0, clearedByte.length);
            }

            pos_ = 0;
            dssLength_ = buffer_.length;

            int lobLength = 0;
            if (clearedByte.length > 32767) {  //for extended length, length is the 4 bytes that follow codepoint
                lobLength = ((clearedByte[4] & 0xFF) << 24) +
                        ((clearedByte[5] & 0xFF) << 16) +
                        ((clearedByte[6] & 0xFF) << 8) +
                        ((clearedByte[7] & 0xFF) << 0);
                longValueForDecryption_ = new byte[lobLength];
                System.arraycopy(clearedByte, 8, longValueForDecryption_, 0, clearedByte.length - 8);
            } else {
                lobLength = ((clearedByte[0] & 0xFF) << 8) +
                        ((clearedByte[1] & 0xFF) << 0);
                longValueForDecryption_ = new byte[lobLength - 4];
                System.arraycopy(clearedByte, 4, longValueForDecryption_, 0, clearedByte.length - 4);
            }
        } else {
            int bytesRead = ensureALayerDataInBuffer(dssLength_);  //we need to get back all the data here, and then decrypt
            if (bytesRead > 0) //we ensuredALayerDAtaInBuffer here and set the flag to true, so we don't need do this again later
            {
                ensuredLengthForDecryption_ = true;
            }
            byte[] encryptedByte = new byte[dssLength_];
            System.arraycopy(buffer_, pos_, encryptedByte, 0, dssLength_);
            byte[] array1 = new byte[pos_];
            System.arraycopy(buffer_, 0, array1, 0, pos_);  //save the data before encrypted data in array1
            byte[] array3 = new byte[buffer_.length - dssLength_ - pos_];
            System.arraycopy(buffer_, pos_ + dssLength_, array3, 0, buffer_.length - dssLength_ - pos_); //save the data follows encrypted data in array3
            byte[] clearedByte = null;
            try {
                clearedByte = netAgent_.netConnection_.getEncryptionManager().decryptData(encryptedByte,
                        NetConfiguration.SECMEC_EUSRIDPWD,
                        netAgent_.netConnection_.getTargetPublicKey(),
                        netAgent_.netConnection_.getTargetPublicKey());
            } catch (SqlException e) {
                //throw new SqlException (agent_.logWriter_, "error in decrypting data");
            }
            dssLength_ -= (encryptedByte.length - clearedByte.length);
            byte[] buffer = new byte[array1.length + clearedByte.length + array3.length];
            System.arraycopy(array1, 0, buffer, 0, array1.length);
            System.arraycopy(clearedByte, 0, buffer, array1.length, clearedByte.length);
            System.arraycopy(array3, 0, buffer, array1.length + clearedByte.length, array3.length);
            buffer_ = buffer;
            int oldCount = count_;
            count_ = count_ - (encryptedByte.length - clearedByte.length);
            if (((clearedByte[2] & 0xff) << 8) + ((clearedByte[3] & 0xff) << 0) == 0x146c) {

                boolean flag = false;
                if (gdsFormatter == 0x54) {
                    flag = true;
                }
                if (flag) {
                    int firstLobLength = ((clearedByte[0] & 0xFF) << 8) +
                        ((clearedByte[1] & 0xFF) << 0);
                    if (oldCount - oldDssLength < 6) {
                        int totalBytesRead = fill(6); //sometimes the 2nd EXTDTA doesn't come back, need to fetch again to get it
                        if (totalBytesRead > 0) {
                            longBufferForDecryption_ = new byte[totalBytesRead];
                            longPosForDecryption_ = 0;
                            System.arraycopy(buffer_, pos_ + firstLobLength, longBufferForDecryption_, 0,
                                    totalBytesRead);
                        }

                    } else {
                        longBufferForDecryption_ = new byte[count_ - pos_ - firstLobLength];
                        longPosForDecryption_ = 0;
                        System.arraycopy(buffer_, pos_ + firstLobLength, longBufferForDecryption_, 0,
                                longBufferForDecryption_.length);

                    }
                } //end if(flag)
                int lobLength = ((clearedByte[0] & 0xFF) << 8) +
                        ((clearedByte[1] & 0xFF) << 0) - 4;

                longValueForDecryption_ = new byte[lobLength];

                System.arraycopy(clearedByte, 4, longValueForDecryption_, 0, clearedByte.length - 4);  //copy the decrypted lob value (excluded length an dcodepoint) to longValue_
            } else if (((clearedByte[2] & 0xff) << 8) + ((clearedByte[3] & 0xff) << 0) == 0x241B) {
                int length = ((clearedByte[0] & 0xFF) << 8) +
                        ((clearedByte[1] & 0xFF) << 0);
                boolean noData = false;
                if (clearedByte[4] == -1 && clearedByte[5] == -1) {
                    noData = true; //there is no data, no need to do the copy
                }
                if (!noData) {
                    if (length == 32776) {
                        length = ((clearedByte[4] & 0xFF) << 24) +
                                ((clearedByte[5] & 0xFF) << 16) +
                                ((clearedByte[6] & 0xFF) << 8) +
                                ((clearedByte[7] & 0xFF) << 0);
                        longValueForDecryption_ = new byte[length];
                        System.arraycopy(clearedByte, 8, longValueForDecryption_, 0,
                                clearedByte.length - 8);
                        longCountForDecryption_ = count_ - (pos_ + length + 8);
                        longBufferForDecryption_ = new byte[buffer_.length - pos_ - length - 8];
                        System.arraycopy(buffer_, pos_ + length + 8, longBufferForDecryption_, 0,
                                longBufferForDecryption_.length);

                    } else {
                        longPosForDecryption_ = 0;
                        longCountForDecryption_ = count_ - (pos_ + length);
                        longBufferForDecryption_ = new byte[buffer_.length - pos_ - length];
                        System.arraycopy(buffer_, pos_ + length, longBufferForDecryption_, 0,
                                longBufferForDecryption_.length);

                        longValueForDecryption_ = new byte[length - 4];

                        System.arraycopy(clearedByte, 4, longValueForDecryption_, 0,
                                clearedByte.length - 4);
                    }
                }
            }
        }
    }


    final int readUnsignedShort() throws DisconnectException {
        // should we be checking dss lengths and ddmScalarLengths here
        // if yes, i am not sure this is the correct place if we should be checking
        ensureBLayerDataInBuffer(2);
        adjustLengths(2);
        return ((buffer_[pos_++] & 0xff) << 8) +
                ((buffer_[pos_++] & 0xff) << 0);
    }

    final short readShort() throws DisconnectException {
        // should we be checking dss lengths and ddmScalarLengths here
        ensureBLayerDataInBuffer(2);
        adjustLengths(2);
        short s = SignedBinary.getShort(buffer_, pos_);

        pos_ += 2;

        return s;
    }

    final int readInt() throws DisconnectException {
        // should we be checking dss lengths and ddmScalarLengths here
        ensureBLayerDataInBuffer(4);
        adjustLengths(4);
        int i = SignedBinary.getInt(buffer_, pos_);
        pos_ += 4;

        return i;
    }

    final int[] readUnsignedShortList() throws DisconnectException {
        int len = ddmScalarLen_;
        ensureBLayerDataInBuffer(len);
        adjustLengths(len);

        int count = len / 2;
        int[] list = new int[count];

        for (int i = 0; i < count; i++) {
            list[i] = ((buffer_[pos_++] & 0xff) << 8) +
                    ((buffer_[pos_++] & 0xff) << 0);
        }

        return list;
    }

    final int readUnsignedByte() throws DisconnectException {
        ensureBLayerDataInBuffer(1);
        adjustLengths(1);
        return (buffer_[pos_++] & 0xff);
    }

    final byte readByte() throws DisconnectException {
        ensureBLayerDataInBuffer(1);
        adjustLengths(1);
        return (byte) (buffer_[pos_++] & 0xff);
    }

    final String readString(int length, Charset encoding)
            throws DisconnectException {
        ensureBLayerDataInBuffer(length);
        adjustLengths(length);
        String s = new String(buffer_, pos_, length, encoding);
        pos_ += length;
        return s;
    }

    final String readString() throws DisconnectException {
        int len = ddmScalarLen_;
        ensureBLayerDataInBuffer(len);
        adjustLengths(len);
        String result = netAgent_.getCurrentCcsidManager()
                            .convertToJavaString(buffer_, pos_, len);
        pos_ += len;
        return result;
    }

    final byte[] readBytes(int length) throws DisconnectException {
        ensureBLayerDataInBuffer(length);
        adjustLengths(length);

        byte[] b = new byte[length];
        System.arraycopy(buffer_, pos_, b, 0, length);
        pos_ += length;
        return b;
    }

    final byte[] readBytes() throws DisconnectException {
        int len = ddmScalarLen_;
        ensureBLayerDataInBuffer(len);
        adjustLengths(len);

        byte[] b = new byte[len];
        System.arraycopy(buffer_, pos_, b, 0, len);
        pos_ += len;
        return b;
    }

    final void skipBytes(int length) throws DisconnectException {
        ensureBLayerDataInBuffer(length);
        adjustLengths(length);
        pos_ += length;
    }

    final void skipBytes() throws DisconnectException {
        int len = ddmScalarLen_;
        ensureBLayerDataInBuffer(len);
        adjustLengths(len);
        pos_ += len;
    }

    // This will be the new and improved getData that handles all QRYDTA/EXTDTA
    // Returns the stream so that the caller can cache it
    final ByteArrayOutputStream getData(ByteArrayOutputStream existingBuffer) throws DisconnectException {
        boolean readHeader;
        int copySize;
        ByteArrayOutputStream baos;

        // note: an empty baos can yield an allocated and empty byte[]
        if (existingBuffer != null) {
            baos = existingBuffer;
        } else {
            if (ddmScalarLen_ != -1) {
                // allocate a stream based on a known amount of data
                baos = new ByteArrayOutputStream(ddmScalarLen_);
            } else {
                // allocate a stream to hold an unknown amount of data
                baos = new ByteArrayOutputStream();
                //isLengthAndNullabilityUnknown = true;
            }
        }

        // set the amount to read for the first segment
        copySize = dssLength_; // note: has already been adjusted for headers

        do {
            // determine if a continuation header needs to be read after the data
            if (dssIsContinued_) {
                readHeader = true;
            } else {
                readHeader = false;
            }

            // read the segment
            ensureALayerDataInBuffer(copySize);
            adjustLengths(copySize);
            baos.write(buffer_, pos_, copySize);
            pos_ += copySize;

            // read the continuation header, if necessary
            if (readHeader) {
                readDSSContinuationHeader();
            }

            copySize = dssLength_;
        } while (readHeader == true);

        return baos;
    }

    // reads a DSS continuation header
    // prereq: pos_ is positioned on the first byte of the two-byte header
    // post:   dssIsContinued_ is set to true if the continuation bit is on, false otherwise
    //         dssLength_ is set to DssConstants.MAX_DSS_LEN - 2 (don't count the header for the next read)
    // helper method for getEXTDTAData
    private void readDSSContinuationHeader() throws DisconnectException {
        ensureALayerDataInBuffer(2);

        dssLength_ =
                ((buffer_[pos_++] & 0xFF) << 8) +
                ((buffer_[pos_++] & 0xFF) << 0);

        if ((dssLength_ & 0x8000) == 0x8000) {
            dssLength_ = DssConstants.MAX_DSS_LEN;
            dssIsContinued_ = true;
        } else {
            dssIsContinued_ = false;
        }
        // it is a syntax error if the dss continuation header length
        // is less than or equal to two
        if (dssLength_ <= 2) {
            doSyntaxrmSemantics(CodePoint.SYNERRCD_DSS_CONT_LESS_OR_EQUAL_2);
        }

        dssLength_ -= 2;  // avoid consuming the DSS cont header
    }


    // As part of parsing the reply, the client can detect that the
    // data sent from the target agent does not structurally
    // conform to the requirements of the DDM architecture.  These are
    // the same checks performed by the target server on the messages
    // it receives from the protocolj code.  Server side detected errors
    // result in a SYNTAXRM being returned from the AS.  According to the
    // DDM manual, parsing of the DSS is terminated when the error is
    // detected.  The Syntax Error Code, SYNERRCD, describes the various errors.
    //
    // Note: Not all of these may be valid at the client.  See descriptions for
    // which ones make sense for client side errors/checks.
    // Syntax Error Code                  Description of Error
    // -----------------                  --------------------
    // 0x01                               Dss header Length is less than 6.
    // 0x02                               Dss header Length does not match the
    //                                    number of bytes of data found.
    // 0x03                               Dss header C-byte not D0.
    // 0x04                               Dss header f-bytes either not
    //                                    recognized or not supported.
    // 0x05                               DSS continuation specified but not found.
    //                                    For example, DSS continuation is specified
    //                                    on the last DSS, and the SNA LU 6.2 communication
    //                                    facility returned the SEND indicator.
    // 0x06                               DSS chaining specified but no DSS found.
    //                                    For example, DSS chaining is specified
    //                                    on the last DSS, and the SNA LU 6.2 communication
    //                                    facility returned the SEND indicator.
    // 0x07                               Object length less than four.  For example,
    //                                    a command parameter's length is specified
    //                                    as two, or a command's length is specified as three.
    // 0x08                               Object length does not match the number of bytes of data
    //                                    found.  For example, a RQSDSS with a length of 150
    //                                    contains a command whose length is 125 or a SRVDGN parameter
    //                                    specifies a length of 200 but there are only 50
    //                                    bytes left in the DSS.
    // 0x09                               Object length greater than maximum allowed.
    //                                    For example, a RECCNT parameter specifies a
    //                                    length of ten, but the parameter is defined
    //                                    to have a maximum length of eight.
    // 0x0A                               Object length less than the minimum required.
    //                                    For example, a SVRCOD parameter specifies a
    //                                    length of five, but the parameter is defined
    //                                    to have a fixed length of six.
    // 0x0B                               Object length not allowed.  For example,
    //                                    a FILEXDPT parameter is specified with a length of
    //                                    11, but this would indicate that only half of the hours
    //                                    field is present instead of the complete hours field.
    // 0x0C                               Incorrect large object extended length field (see
    //                                    description of DSS).  For example, an extended
    //                                    length field is present, but it is only three bytes
    //                                    long when it is defined to be a multiple of two bytes.
    // 0x0D                               Object code point index not supported.
    //                                    For example, a code point of 8032 is encountered
    //                                    but x'8' is a reserved code point index.
    // 0x0E                               Required object not found.  For example, a CLEAR
    //                                    command does not have a filnam parameter present,
    //                                    or a MODREC command is not followed by a RECORD
    //                                    command data object.
    // 0x0F                               Too many command data objects sent.  For example,
    //                                    a MODREC command is followed by two RECORD command
    //                                    command data objects, or a DECREC command is followed
    //                                    by RECORD object.
    // 0x10                               Mutually exclusive objects present.
    //                                    For example, a CRTDIRF command specifies both
    //                                    a DCLNAM and FILNAM parameters.
    // 0x11                               Too few command data objects sent.
    //                                    For example, an INSRECEF command that
    //                                    specified RECCNT95) is followed by only
    //                                    4 RECORD command data objects.
    // 0x12                               Duplicate object present.
    //                                    For example, a LSTFAT command has tow FILNAM
    //                                    parameters specified.
    // 0x13                               Invalid request correlator specified.
    //                                    Use PRCCNVRM with PRCCNVDC of 04 or 05 instead
    //                                    of this error code.  This error code is being retained
    //                                    for compatibility with Level 1 of the architecture.
    // 0x14                               Required value not found.
    // 0x15                               Reserved value not allowed.  For example,
    //                                    a INSRECEF command specified a RECCNT(0) parameter.
    // 0x16                               DSS continuation less than or equal to two.
    //                                    For example, the length bytes of the DSS continuation
    //                                    have the value of one.
    // 0x17                               Objects not in required order.  For example, a RECAL
    //                                    object contains a RECORD object followed by a RECNBR
    //                                    object with is not in the defined order.
    // 0x18                               DSS chaining byt not b'1', but DSSFMT bit3 set to b'1'.
    // 0x19                               Previous DSS indicated current DSS has the same
    //                                    request correlator, but the request correlators are
    //                                    not the same.
    // 0x1A                               DSS cahining bit not b'1', but error continuation requested.
    // 0x1B                               Mutually exclusive parameter values not specified.
    //                                    For example, an OPEN command specified PRPSHD(TRUE)
    //                                    and FILSHR(READER).
    // 0x1D                               Code point not valid command.  For example, the first
    //                                    code point in RQSDSS either is not in the dictionary
    //                                    or is not a code point for a command.
    //
    // When the client detects these errors, it will be handled as if a SYNTAXRM is returned
    // from the server.  In this SYNTAXRM case, PROTOCOL architects an SQLSTATE of 58008 or 58009.
    //
    // Messages
    // SQLSTATE : 58009
    //     Execution failed due to a distribution protocol error that caused deallocation of the conversation.
    //     SQLCODE : -30020
    //     Execution failed because of a Distributed Protocol
    //         Error that will affect the successful execution of subsequent
    //         commands and SQL statements: Reason Code <reason-code>.
    //      Some possible reason codes include:
    //      121C Indicates that the user is not authorized to perform the requested command.
    //      1232 The command could not be completed because of a permanent error.
    //          In most cases, the server will be in the process of an abend.
    //      220A The target server has received an invalid data description.
    //          If a user SQLDA is specified, ensure that the fields are
    //          initialized correctly. Also, ensure that the length does not
    //          exceed the maximum allowed length for the data type being used.
    //
    //      The command or statement cannot be processed.  The current
    //          transaction is rolled back and the application is disconnected
    //          from the remote database.
    final void doSyntaxrmSemantics(int syntaxErrorCode) throws DisconnectException {
        DisconnectException e = new DisconnectException(agent_,
                new ClientMessageId(SQLState.DRDA_CONNECTION_TERMINATED),
                SqlException.getMessageUtil().getTextMessage(
                    MessageId.CONN_DRDA_DATASTREAM_SYNTAX_ERROR,
                    syntaxErrorCode));
            
        // if we are communicating to an older server, we may get a SYNTAXRM on
        // ACCSEC (missing codepoint RDBNAM) if we were unable to convert to
        // EBCDIC (See DERBY-4008/DERBY-4004).  In that case we should chain 
        // the original conversion exception, so it is clear to the user what
        // the problem was.
        if (netAgent_.exceptionConvertingRdbnam != null) {
            e.setNextException(netAgent_.exceptionConvertingRdbnam);
            netAgent_.exceptionConvertingRdbnam = null;
        }
        agent_.accumulateChainBreakingReadExceptionAndThrow(e);
    }


// the names of these methods start with a letter z.
// the z will be removed when they are finalized...

    protected final void pushLengthOnCollectionStack() {
        ddmCollectionLenStack_[++topDdmCollectionStack_] = ddmScalarLen_;
        ddmScalarLen_ = 0;
    }

    protected final void adjustLengths(int length) {
        ddmScalarLen_ -= length;
        adjustCollectionAndDssLengths(length);
        /*
        for (int i = 0; i <= topDdmCollectionStack_; i++) {
          ddmCollectionLenStack_[i] -= length;
        }
        dssLength_ -= length;
        */
    }

    protected int adjustDdmLength(int ddmLength, int length) {
        ddmLength -= length;
        if (ddmLength == 0) {
            adjustLengths(getDdmLength());
        }
        return ddmLength;
    }

    // Pop the collection Length stack.
    // pre:  The collection length stack must not be empty and the top value
    //       on the stack must be 0.
    // post: The top 0 value on the stack will be popped.
    protected final void popCollectionStack() {
        topDdmCollectionStack_--;
    }

    protected final int peekCodePoint() throws DisconnectException {
        if (topDdmCollectionStack_ != EMPTY_STACK) {
            if (ddmCollectionLenStack_[topDdmCollectionStack_] == 0) {
                return END_OF_COLLECTION;
            } else if (ddmCollectionLenStack_[topDdmCollectionStack_] < 4) {
                // error
            }
        }

        // if there is no more data in the current dss, and the dss is not
        // continued, indicate the end of the same Id chain or read the next dss header.
        if ((dssLength_ == 0) && (!dssIsContinued_)) {
            if (!dssIsChainedWithSameID_) {
                return END_OF_SAME_ID_CHAIN;
            }
            readDssHeader();
        }

        if (longBufferForDecryption_ == null)  //we don't need to do this if it's data stream encryption
        {
            ensureBLayerDataInBuffer(4);
        }
        peekedLength_ = ((buffer_[pos_] & 0xff) << 8) + ((buffer_[pos_ + 1] & 0xff) << 0);
        peekedCodePoint_ = ((buffer_[pos_ + 2] & 0xff) << 8) + ((buffer_[pos_ + 3] & 0xff) << 0);

        // check for extended length
        if ((peekedLength_ & 0x8000) == 0x8000) {
            peekExtendedLength();
        } else {
            peekedNumOfExtendedLenBytes_ = 0;
        }
        return peekedCodePoint_;
    }

    // Read "length" number of bytes from the buffer into the byte array b starting from offset
    // "offset".  The current offset in the buffer does not change.
    protected final int peekFastBytes(byte[] b, int offset, int length) throws DisconnectException {
        for (int i = 0; i < length; i++) {
            b[offset + i] = buffer_[pos_ + i];
        }
        return offset + length;
    }

    protected final void parseLengthAndMatchCodePoint(int expectedCodePoint) throws DisconnectException {
        int actualCodePoint = 0;
        if (peekedCodePoint_ == END_OF_COLLECTION) {
            actualCodePoint = readLengthAndCodePoint();
        } else {
            actualCodePoint = peekedCodePoint_;
            pos_ += (4 + peekedNumOfExtendedLenBytes_);
            ddmScalarLen_ = peekedLength_;
            if (peekedNumOfExtendedLenBytes_ == 0 && ddmScalarLen_ != -1) {
                adjustLengths(4);
            } else {
                adjustCollectionAndDssLengths(4 + peekedNumOfExtendedLenBytes_);
            }
            peekedLength_ = 0;
            peekedCodePoint_ = END_OF_COLLECTION;
            peekedNumOfExtendedLenBytes_ = 0;
        }

        if (actualCodePoint != expectedCodePoint) {
            agent_.accumulateChainBreakingReadExceptionAndThrow(
                new DisconnectException(agent_, 
                    new ClientMessageId(SQLState.NET_NOT_EXPECTED_CODEPOINT), 
                    actualCodePoint, expectedCodePoint));
        }
    }

    private int readLengthAndCodePoint() throws DisconnectException {
        if (topDdmCollectionStack_ != EMPTY_STACK) {
            if (ddmCollectionLenStack_[topDdmCollectionStack_] == 0) {
                return END_OF_COLLECTION;
            } else if (ddmCollectionLenStack_[topDdmCollectionStack_] < 4) {
                agent_.accumulateChainBreakingReadExceptionAndThrow(
                    new DisconnectException(agent_, 
                    new ClientMessageId(SQLState.NET_DDM_COLLECTION_TOO_SMALL)));
            }
        }

        // if there is no more data in the current dss, and the dss is not
        // continued, indicate the end of the same Id chain or read the next dss header.
        if ((dssLength_ == 0) && (!dssIsContinued_)) {
            if (!dssIsChainedWithSameID_) {
                return END_OF_SAME_ID_CHAIN;
            }
            readDssHeader();
        }

        ensureBLayerDataInBuffer(4);
        ddmScalarLen_ =
                ((buffer_[pos_++] & 0xff) << 8) +
                ((buffer_[pos_++] & 0xff) << 0);
        int codePoint = ((buffer_[pos_++] & 0xff) << 8) +
                ((buffer_[pos_++] & 0xff) << 0);
        adjustLengths(4);

        // check for extended length
        if ((ddmScalarLen_ & 0x8000) == 0x8000) {
            readExtendedLength();
        }
        return codePoint;
    }

    private void readExtendedLength() throws DisconnectException {
        int numberOfExtendedLenBytes = (ddmScalarLen_ - 0x8000); // fix scroll problem was - 4
        int adjustSize = 0;
        switch (numberOfExtendedLenBytes) {
        case 4:
            ensureBLayerDataInBuffer(4);
            ddmScalarLen_ =
                    ((buffer_[pos_++] & 0xff) << 24) +
                    ((buffer_[pos_++] & 0xff) << 16) +
                    ((buffer_[pos_++] & 0xff) << 8) +
                    ((buffer_[pos_++] & 0xff) << 0);
            adjustSize = 4;
            break;
        case 0:
            ddmScalarLen_ = -1;
            adjustSize = 0;
            break;
        default:
            doSyntaxrmSemantics(CodePoint.SYNERRCD_INCORRECT_EXTENDED_LEN);
        }

        adjustCollectionAndDssLengths(adjustSize);
        /*
        // adjust the lengths here.  this is a special case since the
        // extended length bytes do not include their own length.
        for (int i = 0; i <= topDdmCollectionStack_; i++) {
          ddmCollectionLenStack_[i] -= adjustSize;
        }
        dssLength_ -= adjustSize;
        */
    }

    private void adjustCollectionAndDssLengths(int length) {
        // adjust the lengths here.  this is a special case since the
        // extended length bytes do not include their own length.
        for (int i = 0; i <= topDdmCollectionStack_; i++) {
            ddmCollectionLenStack_[i] -= length;
        }
        dssLength_ -= length;
    }

    protected final void startSameIdChainParse() throws DisconnectException {
        readDssHeader();
        netAgent_.clearSvrcod();
    }

    protected final void endOfSameIdChainData() throws DisconnectException {
        netAgent_.targetTypdef_ = netAgent_.originalTargetTypdef_;
        netAgent_.targetSqlam_ = netAgent_.orignalTargetSqlam_;

        if (this.topDdmCollectionStack_ != Reply.EMPTY_STACK) {
            agent_.accumulateChainBreakingReadExceptionAndThrow(
                new DisconnectException(agent_, 
                new ClientMessageId(SQLState.NET_COLLECTION_STACK_NOT_EMPTY)));
        }
        if (this.dssLength_ != 0) {
            agent_.accumulateChainBreakingReadExceptionAndThrow(
                new DisconnectException(agent_, 
                new ClientMessageId(SQLState.NET_DSS_NOT_ZERO)));
        }
        if (dssIsChainedWithSameID_ == true) {
            agent_.accumulateChainBreakingReadExceptionAndThrow(
                new DisconnectException(agent_, 
                new ClientMessageId(SQLState.NET_DSS_CHAINED_WITH_SAME_ID)));
        }
    }
    
    protected final int peekTotalColumnCount(int tripletLength) throws DisconnectException {
        int columnCount = 0;
        int offset = 0;
        int tripletType = FdocaConstants.CPT_TRIPLET_TYPE;
        while (tripletType == FdocaConstants.CPT_TRIPLET_TYPE) {
            columnCount += ((tripletLength - 3) / 3);
            // Peek ahead for the next triplet's tripletLength and tripletType.
            // The number of bytes to skip before the next tripletType is tripletLength - 3.
            ensureBLayerDataInBuffer(tripletLength - 3);
            offset += (tripletLength - 3);
            tripletLength = (buffer_[pos_ + offset++] & 0xff);
            tripletType = (buffer_[pos_ + offset++] & 0xff);
            // Skip the 1-byte tripletId.
            offset++;
        }
        return columnCount;
    }

    private void peekExtendedLength() throws DisconnectException {
        peekedNumOfExtendedLenBytes_ = (peekedLength_ - 0x8004);
        switch (peekedNumOfExtendedLenBytes_) {
        case 4:
            // L   L   C   P  Extended Length
            // -->2-bytes<--  --->4-bytes<---
            // We are only peeking the length here, the actual pos_ is still before LLCP.  We ensured
            // 4-bytes in peedCodePoint() for the LLCP, and we need to ensure 4-bytes(of LLCP) + the
            // extended length bytes here.
            if (longBufferForDecryption_ == null) //we ddon't need to do this if it's data stream encryption
            {
                ensureBLayerDataInBuffer(4 + 4);
            }
            // The ddmScalarLen_ we peek here does not include the LLCP and the extended length bytes
            // themselves.  So we will add those back to the ddmScalarLen_ so it can be adjusted
            // correctly in parseLengthAndMatchCodePoint(). (since the adjustLengths() method will
            // subtract the length from ddmScalarLen_)
            peekedLength_ =
                    ((buffer_[pos_ + 4] & 0xff) << 24) +
                    ((buffer_[pos_ + 5] & 0xff) << 16) +
                    ((buffer_[pos_ + 6] & 0xff) << 8) +
                    ((buffer_[pos_ + 7] & 0xff) << 0);
            break;
        case 0:
            peekedLength_ = -1; // this ddm is streamed, so set -1 -> length unknown
            break;
        default:
            doSyntaxrmSemantics(CodePoint.SYNERRCD_INCORRECT_EXTENDED_LEN);
        }
    }

    final int readFastUnsignedByte() throws DisconnectException {
        return (buffer_[pos_++] & 0xff);
    }

    final short readFastShort() throws DisconnectException {
        short s = SignedBinary.getShort(buffer_, pos_);
        pos_ += 2;
        return s;
    }

    final int readFastUnsignedShort() throws DisconnectException {
        return ((buffer_[pos_++] & 0xff) << 8) +
                ((buffer_[pos_++] & 0xff) << 0);
    }

    final int readFastInt() throws DisconnectException {
        int i = SignedBinary.getInt(buffer_, pos_);
        pos_ += 4;
        return i;
    }

    final String readFastString(int length) throws DisconnectException {
        String result = netAgent_.getCurrentCcsidManager()
                            .convertToJavaString(buffer_, pos_, length);
        pos_ += length;
        return result;
    }

    final byte[] readFastBytes(int length) throws DisconnectException {
        byte[] b = new byte[length];
        System.arraycopy(buffer_, pos_, b, 0, length);
        pos_ += length;
        return b;
    }

    protected final int peekFastLength() throws DisconnectException {
        return (((buffer_[pos_] & 0xff) << 8) +
                ((buffer_[pos_ + 1] & 0xff) << 0));
    }

    final void skipFastBytes(int length) throws DisconnectException {
        pos_ += length;
    }

    final void readFastIntArray(int[] array) throws DisconnectException {
        for (int i = 0; i < array.length; i++) {
            array[i] = SignedBinary.getInt(buffer_, pos_);
            pos_ += 4;
        }
    }

    final String readFastString(int length, Charset encoding) {
        String s = new String(buffer_, pos_, length, encoding);
        pos_ += length;
        return s;
    }

    final byte[] readFastLDBytes() throws DisconnectException {
        int len = ((buffer_[pos_++] & 0xff) << 8) + ((buffer_[pos_++] & 0xff) << 0);
        if (len == 0) {
            return null;
        }

        byte[] b = new byte[len];
        System.arraycopy(buffer_, pos_, b, 0, len);
        pos_ += len;
        return b;
    }

    final long readFastLong() throws DisconnectException {
        long l = SignedBinary.getLong(buffer_, pos_);
        pos_ += 8;
        return l;
    }

    final byte readFastByte() throws DisconnectException {
        return (byte) (buffer_[pos_++] & 0xff);
    }

    final void mark() {
        currentPos_ = pos_;
    }

    // remove and return the top offset value from mark stack.
    private int popMark() {
        return currentPos_;
    }

    final int getFastSkipSQLCARDrowLength() {
        return pos_ - popMark();
    }

    // The only difference between this method and the original getData() method is this method
    // is not doing an ensureALayerDataInBuffer
    final ByteArrayOutputStream getFastData(ByteArrayOutputStream existingBuffer) throws DisconnectException {
        boolean readHeader;
        int copySize;
        ByteArrayOutputStream baos;

        // note: an empty baos can yield an allocated and empty byte[]
        if (existingBuffer != null) {
            baos = existingBuffer;
        } else {
            if (ddmScalarLen_ != -1) {
                // allocate a stream based on a known amount of data
                baos = new ByteArrayOutputStream(ddmScalarLen_);
            } else {
                // allocate a stream to hold an unknown amount of data
                baos = new ByteArrayOutputStream();
                //isLengthAndNullabilityUnknown = true;
            }
        }

        // set the amount to read for the first segment
        copySize = dssLength_; // note: has already been adjusted for headers

        do {
            // determine if a continuation header needs to be read after the data
            if (dssIsContinued_) {
                readHeader = true;
            } else {
                readHeader = false;
            }

            // read the segment
            //ensureALayerDataInBuffer (copySize);
            adjustLengths(copySize);
            baos.write(buffer_, pos_, copySize);
            pos_ += copySize;

            // read the continuation header, if necessary
            if (readHeader) {
                readDSSContinuationHeader();
            }

            copySize = dssLength_;
        } while (readHeader == true);

        return baos;
    }

    // This method is only used to match the codePoint for those class instance variables
    // that are embedded in other reply messages.
    final protected void matchCodePoint(int expectedCodePoint) throws DisconnectException {
        int actualCodePoint = 0;
        actualCodePoint = peekedCodePoint_;
        pos_ += 4;
        if (actualCodePoint != expectedCodePoint) {
            agent_.accumulateChainBreakingReadExceptionAndThrow(
                new DisconnectException(agent_, 
                    new ClientMessageId(SQLState.NET_NOT_EXPECTED_CODEPOINT), 
                    actualCodePoint, expectedCodePoint));
        }
    }


    protected final int peekNumOfColumns() throws DisconnectException {
        // skip the 4-byte LLCP and any extended length bytes + 1-byte null sqlcagrp null indicator
        int offset = (4 + peekedNumOfExtendedLenBytes_ + 1);

        offset = skipSQLDHROW(offset);

        return SignedBinary.getShort(buffer_, pos_ + offset);
    }

    protected final boolean peekForNullSqlcagrp() {
        // skip the 4-byte LLCP and any extended length bytes
        int offset = (4 + peekedNumOfExtendedLenBytes_);
        int nullInd = buffer_[pos_ + offset] & 0xff;
        return (nullInd == CodePoint.NULLDATA);
    }

    private int skipSQLDHROW(int offset) {
        int sqldhrowgrpNullInd = buffer_[pos_ + offset++] & 0xff;
        if (sqldhrowgrpNullInd == CodePoint.NULLDATA) {
            return offset;
        }

        offset += 12;

        // skip sqldrdbnam
        int stringLength = ((buffer_[pos_ + offset++] & 0xff) << 8) +
                ((buffer_[pos_ + offset++] & 0xff) << 0);
        offset += stringLength;

        // skip sqldschema
        stringLength = ((buffer_[pos_ + offset++] & 0xff) << 8) +
                ((buffer_[pos_ + offset++] & 0xff) << 0);
        offset += stringLength;

        stringLength = ((buffer_[pos_ + offset++] & 0xff) << 8) +
                ((buffer_[pos_ + offset++] & 0xff) << 0);
        offset += stringLength;

        return offset;
    }
}




