📄 ddmwriter.java
字号:
// start of the segment and set the value of the 2-byte DSS // continuation header, which needs to hold the length of // this segment's data, together with the continuation flag // if this is not the rightmost (passOne) segment. // // In general, each segment except the rightmost will contain // 32765 bytes of data, plus the 2-byte header, and its // continuation flag will be set, so the header value will // be 0xFFFF. The rightmost segment will not have the // continuation flag set, so its value may be anything from // 0x0001 to 0x7FFF, depending on the amount of data in that // segment. // // Note that the 0th (leftmost) segment also has a 2-byte // DSS header, which needs to have its continuation flag set. // This is done by resetting the "totalSize" variable below, // at which point that variable no longer holds the total size // of the object, but rather just the length of segment 0. The // total size of the object was written using extended length // bytes by the endDdm() method earlier. // // Additional information about this routine is available in the // bug notes for DERBY-125: // http://issues.apache.org/jira/browse/DERBY-125 // mark passOne to help with calculating the length of the final (first or // rightmost) continuation header. boolean passOne = true; do { // calculate chunk of data to shift int dataToShift = bytesRequiringContDssHeader % 32765; if (dataToShift == 0) dataToShift = 32765; int startOfCopyData = dataByte - dataToShift + 1; System.arraycopy(bytes,startOfCopyData, bytes, startOfCopyData + shiftSize, dataToShift); dataByte -= dataToShift; // calculate the value the value of the 2 byte continuation dss // header which includes the length of itself. On the first pass, // if the length is 32767 // we do not want to set the continuation dss header flag. int twoByteContDssHeader = dataToShift + 2; if (passOne) passOne = false; else { if (twoByteContDssHeader == DssConstants.MAX_DSS_LENGTH) twoByteContDssHeader = (twoByteContDssHeader | DssConstants.CONTINUATION_BIT); } // insert the header's length bytes bytes[dataByte + shiftSize - 1] = (byte) ((twoByteContDssHeader >>> 8) & 0xff); bytes[dataByte + shiftSize] = (byte) (twoByteContDssHeader & 0xff); // adjust the bytesRequiringContDssHeader and the amount to shift for // data in upstream headers. bytesRequiringContDssHeader -= dataToShift; shiftSize -= 2; // shift and insert another header for more data. } while (bytesRequiringContDssHeader > 0); // set the continuation dss header flag on for the first header totalSize = (DssConstants.MAX_DSS_LENGTH | DssConstants.CONTINUATION_BIT); } // insert the length bytes in the 6 byte dss header. bytes[dssLengthLocation] = (byte) ((totalSize >>> 8) & 0xff); bytes[dssLengthLocation + 1] = (byte) (totalSize & 0xff); } protected void writeExtendedLength(long size) { int numbytes = calculateExtendedLengthByteCount(size); if (size > 0) writeInt(0x8000 | numbytes); else writeInt(numbytes); } /** * Calculate extended length byte count which follows the DSS header * for extended DDM. * * @param ddmSize - size of DDM command * @return minimum number of extended length bytes needed. 0 indicates no * extended length needed. */ private int calculateExtendedLengthByteCount (long ddmSize) { if (ddmSize <= 0x7fff) return 0; // JCC does not support 2 at this time, so we always send // at least 4 // else if (ddmSize <= 0xffff) // return 2; else if (ddmSize <= 0xffffffffL) return 4; else if (ddmSize <= 0xffffffffffffL) return 6; else if (ddmSize <= 0x7fffffffffffffffL) return 8; else // shouldn't happen // XXX - add sanity debug stuff here return 0; } /** * Ensure that there is space in the buffer * * @param length space required */ private void ensureLength (int length) { length += offset; if (length > bytes.length) { if (SanityManager.DEBUG) { agent.trace("DANGER - Expensive expansion of buffer"); } byte newBytes[] = new byte[Math.max (bytes.length << 1, length)]; System.arraycopy (bytes, 0, newBytes, 0, offset); bytes = newBytes; } } /** * Write a Java <code>java.math.BigDecimal</code> to packed decimal bytes. * * @param b BigDecimal to write * @param precision Precision of decimal or numeric type * @return length written. * * @exception SQLException Thrown if # digits > 31 */ private int bigDecimalToPackedDecimalBytes (java.math.BigDecimal b, int precision, int scale) throws SQLException { int declaredPrecision = precision; int declaredScale = scale; // packed decimal may only be up to 31 digits. if (declaredPrecision > 31) // this is a bugcheck only !!! { clearDdm (); throw new java.sql.SQLException ("Packed decimal may only be up to 31 digits!"); } // get absolute unscaled value of the BigDecimal as a String. String unscaledStr = b.unscaledValue().abs().toString(); // get precision of the BigDecimal. int bigPrecision = unscaledStr.length(); if (bigPrecision > 31) { clearDdm (); throw new SQLException ("The numeric literal \"" + b.toString() + "\" is not valid because its value is out of range.", "42820", -405); } int bigScale = b.scale(); int bigWholeIntegerLength = bigPrecision - bigScale; if ( (bigWholeIntegerLength > 0) && (!unscaledStr.equals ("0")) ) { // if whole integer part exists, check if overflow. int declaredWholeIntegerLength = declaredPrecision - declaredScale; if (bigWholeIntegerLength > declaredWholeIntegerLength) { clearDdm (); throw new SQLException ("Overflow occurred during numeric data type conversion of \"" + b.toString() + "\".", "22003", -413); } } // convert the unscaled value to a packed decimal bytes. // get unicode '0' value. int zeroBase = '0'; // start index in target packed decimal. int packedIndex = declaredPrecision-1; // start index in source big decimal. int bigIndex; if (bigScale >= declaredScale) { // If target scale is less than source scale, // discard excessive fraction. // set start index in source big decimal to ignore excessive fraction. bigIndex = bigPrecision-1-(bigScale-declaredScale); if (bigIndex < 0) { // all digits are discarded, so only process the sign nybble. bytes[offset+(packedIndex+1)/2] = (byte) ( (b.signum()>=0)?12:13 ); // sign nybble } else { // process the last nybble together with the sign nybble. bytes[offset+(packedIndex+1)/2] = (byte) ( ( (unscaledStr.charAt(bigIndex)-zeroBase) << 4 ) + // last nybble ( (b.signum()>=0)?12:13 ) ); // sign nybble } packedIndex-=2; bigIndex-=2; } else { // If target scale is greater than source scale, // pad the fraction with zero. // set start index in source big decimal to pad fraction with zero. bigIndex = declaredScale-bigScale-1; // process the sign nybble. bytes[offset+(packedIndex+1)/2] = (byte) ( (b.signum()>=0)?12:13 ); // sign nybble for (packedIndex-=2, bigIndex-=2; bigIndex>=0; packedIndex-=2, bigIndex-=2) bytes[offset+(packedIndex+1)/2] = (byte) 0; if (bigIndex == -1) { bytes[offset+(packedIndex+1)/2] = (byte) ( (unscaledStr.charAt(bigPrecision-1)-zeroBase) << 4 ); // high nybble packedIndex-=2; bigIndex = bigPrecision-3; } else { bigIndex = bigPrecision-2; } } // process the rest. for (; bigIndex>=0; packedIndex-=2, bigIndex-=2) { bytes[offset+(packedIndex+1)/2] = (byte) ( ( (unscaledStr.charAt(bigIndex)-zeroBase) << 4 ) + // high nybble ( unscaledStr.charAt(bigIndex+1)-zeroBase ) ); // low nybble } // process the first nybble when there is one left. if (bigIndex == -1) { bytes[offset+(packedIndex+1)/2] = (byte) (unscaledStr.charAt(0) - zeroBase); packedIndex-=2; } // pad zero in front of the big decimal if necessary. for (; packedIndex>=-1; packedIndex-=2) bytes[offset+(packedIndex+1)/2] = (byte) 0; return declaredPrecision/2 + 1; } /*** * Prepend zeros to numeric string * * @param s string * @param precision - length of padded string * * @return zero padded string */ public static String zeroPadString(String s, int precision) { if (s == null) return s; int slen = s.length(); if (precision == slen) return s; else if (precision > slen) { char[] ca = new char[precision - slen]; Arrays.fill(ca,0,precision - slen,'0'); return new String(ca) + s; } else { // Shouldn't happen but just in case // truncate return s.substring(0,precision); } } private void sendBytes (java.io.OutputStream socketOutputStream) throws java.io.IOException { resetChainState(); try { socketOutputStream.write (bytes, 0, offset); socketOutputStream.flush(); } finally { if ((dssTrace != null) && dssTrace.isComBufferTraceOn()) { dssTrace.writeComBufferData (bytes, 0, offset, DssTrace.TYPE_TRACE_SEND, "Reply", "flush", 5); } clearBuffer(); } } private static int min (int i, int j) { return (i < j) ? i : j; } protected String toDebugString(String indent) { String s = indent + "***** DDMWriter toDebugString ******\n"; int byteslen = 0; if ( bytes != null) byteslen = bytes.length; s += indent + "byte array length = " + bytes.length + "\n"; return s; } /** * Reset any chaining state that needs to be reset * at time of the send */ protected void resetChainState() { prevHdrLocation = -1; } /** * Looks at chaining info for previous DSS written, and use * that to figure out what the correlation id for the current * DSS should be. Return that correlation id. */ private int getCorrelationID() { int cId; if (previousCorrId != DssConstants.CORRELATION_ID_UNKNOWN) { if (previousChainByte == DssConstants.DSSCHAIN_SAME_ID) // then we have to use the last correlation id we sent. cId = previousCorrId; else // get correlation id as normal. cId = nextCorrelationID++; } else { // must be the case that this is the first DSS we're // writing for this connection (because we haven't // called "endDss" yet). So, get the corr id as // normal. cId = nextCorrelationID++; } return cId; } /** * Finalize the current DSS chain and send it if * needed. * * Updates the chaining state of the most recently-written- * to-buffer DSS to correspond to the most recently-read- * from-client request. If that chaining state indicates * we've reached the end of a chain, then we go ahead * and send the buffer across the wire. * @param socketOutputStream Output stream to which we're flushing. */ protected void finalizeChain(byte currChainByte, OutputStream socketOutputStream) throws DRDAProtocolException { // Go back to previous DSS and override the default // chain state (WITH_SAME_ID) with whatever the last // request dictates. if (prevHdrLocation != -1) { // Note: == -1 => the previous DSS was already sent; this // should only happen in cases where the buffer filled up // and we had to send it (which means we were probably // writing EXTDTA). In such cases, proper chaining // should already have been handled @ time of send. bytes[prevHdrLocation + 3] &= 0x0F; // Zero out old chain value. bytes[prevHdrLocation + 3] |= currChainByte; } // previousChainByte needs to match what we just did. previousChainByte = currChainByte; if (currChainByte != DssConstants.DSS_NOCHAIN) // then we're still inside a chain, so don't send. return; // Else, we just ended the chain, so send it across. if ((SanityManager.DEBUG) && (agent != null)) agent.trace("Sending data"); resetChainState(); if (offset != 0) { try { flush(socketOutputStream); } catch (java.io.IOException e) { agent.markCommunicationsFailure( "DDMWriter.finalizeChain()", "OutputStream.flush()", e.getMessage(),"*"); } } } /** * Takes note of the location of the most recently completed * DSS in the buffer, and then returns the current offset. * This method is used in conjunction with "clearDSSesBackToMark" * to allow for DRDAConnThread to "back-out" DSSes in the * event of errors. */ protected int markDSSClearPoint() { lastDSSBeforeMark = prevHdrLocation; return offset; } /** * Does a logical "clear" of everything written to the buffer after * the received mark. It's assumed that this method will be used * in error cases when we've started writing one or more DSSes, * but then hit an error and need to back out. After backing out, * we'll always need to write _something_ back to the client to * indicate an error (typically, we just write an SQLCARD) but what * exactly gets written is handled in DRDAConnThread. Here, we * just do the necessary prep so that whatever comes next will * succeed. */ protected void clearDSSesBackToMark(int mark) { // Logical clear. offset = mark; // Because we've just cleared out the most recently- // written DSSes, we have to make sure the next thing // we write will have the correct correlation id. We // do this by setting the value of 'nextCorrelationID' // based on the chaining byte from the last remaining // DSS (where "remaining" means that it still exists // in the buffer after the clear). if (lastDSSBeforeMark == -1) // we cleared out the entire buffer; reset corr id. nextCorrelationID = 1; else { // last remaining DSS had chaining, so we set "nextCorrelationID" // to be 1 greater than whatever the last remaining DSS had as // its correlation id. nextCorrelationID = 1 + (int) (((bytes[lastDSSBeforeMark + 4] & 0xff) << 8) + (bytes[lastDSSBeforeMark + 5] & 0xff)); } }}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -