You are viewing a plain text version of this content. The canonical link for it is here.
Posted to derby-commits@db.apache.org by ka...@apache.org on 2007/07/19 09:11:21 UTC

svn commit: r557506 - in /db/derby/code/trunk/java/drda/org/apache/derby/impl/drda: DDMWriter.java NetworkServerControlImpl.java

Author: kahatlen
Date: Thu Jul 19 00:11:19 2007
New Revision: 557506

URL: http://svn.apache.org/viewvc?view=rev&rev=557506
Log:
DERBY-2936: Use java.nio.ByteBuffer for buffering in DDMWriter

Use java.nio.charset.CharsetEncoder instead of String.getBytes() to
encode strings.

Modified:
    db/derby/code/trunk/java/drda/org/apache/derby/impl/drda/DDMWriter.java
    db/derby/code/trunk/java/drda/org/apache/derby/impl/drda/NetworkServerControlImpl.java

Modified: db/derby/code/trunk/java/drda/org/apache/derby/impl/drda/DDMWriter.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/drda/org/apache/derby/impl/drda/DDMWriter.java?view=diff&rev=557506&r1=557505&r2=557506
==============================================================================
--- db/derby/code/trunk/java/drda/org/apache/derby/impl/drda/DDMWriter.java (original)
+++ db/derby/code/trunk/java/drda/org/apache/derby/impl/drda/DDMWriter.java Thu Jul 19 00:11:19 2007
@@ -24,7 +24,6 @@
 import java.io.OutputStream;
 import java.io.InputStream;
 import java.io.BufferedOutputStream;
-import java.io.UnsupportedEncodingException;
 import org.apache.derby.iapi.services.sanity.SanityManager;
 import java.sql.SQLException;
 import java.math.BigDecimal;
@@ -34,6 +33,10 @@
 
 import java.io.IOException;
 import java.nio.ByteBuffer;
+import java.nio.CharBuffer;
+import java.nio.charset.CharsetEncoder;
+import java.nio.charset.CoderResult;
+import java.nio.charset.CodingErrorAction;
 
 /**
 	The DDMWriter is used to write DRDA protocol.   The DRDA Protocol is
@@ -52,12 +55,6 @@
 	// Default buffer size
 	private final static int DEFAULT_BUFFER_SIZE = 32767;
 
-
-	static final BigDecimal ZERO = BigDecimal.valueOf(0L);
-        
-	private static final byte MULTI_BYTE_MASK = (byte) 0xC0;
-	private static final byte CONTINUATION_BYTE = (byte) 0x80;
-
 	/**
 	 * Output buffer.
 	 * @see #bytes
@@ -119,20 +116,8 @@
 	// that immediately precedes the mark.
 	private int lastDSSBeforeMark;
 
-	// Constructors
-	DDMWriter (int minSize, CcsidManager ccsidManager, DRDAConnThread agent, DssTrace dssTrace)
-	{
-		this.bytes = new byte[minSize];
-		this.buffer = ByteBuffer.wrap(bytes);
-		this.ccsidManager = ccsidManager;
-		this.agent = agent;
-		this.prevHdrLocation = -1;
-		this.previousCorrId = DssConstants.CORRELATION_ID_UNKNOWN;
-		this.previousChainByte = DssConstants.DSS_NOCHAIN;
-		this.isContinuationDss = false;
-		this.lastDSSBeforeMark = -1;
-		reset(dssTrace);
-	}
+	/** Encoder which encodes strings with the server's default encoding. */
+	private final CharsetEncoder encoder;
 
 	DDMWriter (CcsidManager ccsidManager, DRDAConnThread agent, DssTrace dssTrace)
 	{
@@ -146,6 +131,11 @@
 		this.isContinuationDss = false;
 		this.lastDSSBeforeMark = -1;
 		reset(dssTrace);
+		// create an encoder which inserts the charset's default replacement
+		// character for characters it can't encode
+		encoder = NetworkServerControlImpl.DEFAULT_CHARSET.newEncoder()
+			.onMalformedInput(CodingErrorAction.REPLACE)
+			.onUnmappableCharacter(CodingErrorAction.REPLACE);
 	}
 
 	/**
@@ -1146,6 +1136,20 @@
 		writeLDString(s,0);
 	}
 
+	/**
+	 * Find the maximum number of bytes needed to represent the string in the
+	 * default encoding.
+	 *
+	 * @param s the string to encode
+	 * @return an upper limit for the number of bytes needed to encode the
+	 * string
+	 */
+	private int maxEncodedLength(String s) {
+		// maxBytesPerChar() returns a float, which can only hold 24 bits of an
+		// integer. Therefore, promote the float to a double so that all bits
+		// are preserved in the intermediate result.
+		return (int) (s.length() * (double) encoder.maxBytesPerChar());
+	}
 
 	/**
 	 * Write length delimited string
@@ -1156,54 +1160,39 @@
 	 */
 	protected void writeLDString(String s, int index) throws DRDAProtocolException
 	{
-		try {
-			byte [] byteval = s.getBytes(NetworkServerControlImpl.DEFAULT_ENCODING);
-			int origLen = byteval.length;
-			int writeLen =
-				java.lang.Math.min(FdocaConstants.LONGVARCHAR_MAX_LEN,
-								   origLen);
-			/*
-			Need to make sure we truncate on character boundaries.
-			We are assuming
-			http://www.sun.com/developers/gadc/technicalpublications/articles/utf8.html
-			To find the beginning of a multibyte character:
-			1) Does the current byte start with the bit pattern 10xxxxxx?
-			2) If yes, move left and go to step #1.
-			3) Finished
-			We assume that NetworkServerControlImpl.DEFAULT_ENCODING remains UTF-8
-			*/
-
-			if (SanityManager.DEBUG)
-			{
-				if (!(NetworkServerControlImpl.DEFAULT_ENCODING.equals("UTF8")))
-					SanityManager.THROWASSERT("Encoding assumed to be UTF8, but is actually" + NetworkServerControlImpl.DEFAULT_ENCODING);
-			}
+		// Position on which to write the length of the string (in bytes). The
+		// actual writing of the length is delayed until we have encoded the
+		// string.
+		final int lengthPos = buffer.position();
+		// Position on which to start writing the string (right after length,
+		// which is 2 bytes long).
+		final int stringPos = lengthPos + 2;
+		// don't send more than LONGVARCHAR_MAX_LEN bytes
+		final int maxStrLen =
+			Math.min(maxEncodedLength(s), FdocaConstants.LONGVARCHAR_MAX_LEN);
+
+		ensureLength(2 + maxStrLen);
+
+		// limit the writable area of the output buffer
+		buffer.position(stringPos);
+		buffer.limit(stringPos + maxStrLen);
+
+		// encode the string
+		CharBuffer input = CharBuffer.wrap(s);
+		CoderResult res = encoder.encode(input, buffer, true);
+		if (SanityManager.DEBUG) {
+			// UNDERFLOW is returned if the entire string was encoded, OVERFLOW
+			// is returned if the string was truncated at LONGVARCHAR_MAX_LEN
+			SanityManager.ASSERT(
+				res == CoderResult.UNDERFLOW || res == CoderResult.OVERFLOW,
+				"Unexpected coder result: " + res);
+		}
 
-			if (writeLen != origLen) {
-				//find the first byte of the multibyte char in case
-				//the last byte is part of a multibyte char
-				while (isContinuationChar (byteval [writeLen])) {
-					writeLen--;
-				}
-				//
-				// Now byteval[ writeLen ] is either a standalone 1-byte char
-				// or the first byte of a multi-byte character. That means that
-				// byteval[ writeLen -1 ] is the last (perhaps only) byte of the
-				// previous character.
-				//
-			}
-                        
+		// write the length in bytes
+		buffer.putShort(lengthPos, (short) (maxStrLen - buffer.remaining()));
 
-			writeShort(writeLen);
-			writeBytes(byteval,writeLen);
-		}
-		catch (UnsupportedEncodingException e) {
-			//this should never happen
-			agent.agentError("Encoding " + NetworkServerControlImpl.DEFAULT_ENCODING + " not supported");
-		}
-	}
-	private boolean isContinuationChar( byte b ) {    
-		return ( (b & MULTI_BYTE_MASK) == CONTINUATION_BYTE );
+		// remove the limit on the output buffer
+		buffer.limit(buffer.capacity());
 	}
 
 	/**
@@ -1215,38 +1204,12 @@
 	 */
 	protected void writeString(String s) throws DRDAProtocolException
 	{
-		try {
-			writeBytes(s.getBytes(NetworkServerControlImpl.DEFAULT_ENCODING));
-		} catch (UnsupportedEncodingException e) {
-			//this should never happen
-			agent.agentError("Encoding " + NetworkServerControlImpl.DEFAULT_ENCODING + " not supported");
-		}
-	}
-
-	/**
-	 * Write string with default encoding and specified length
-	 *
-	 * @param s value to be written
-	 * @param length number of bytes to be written
-	 *
-	 * @exception DRDAProtocolException
-	 */
-	protected void writeString(String s, int length) throws DRDAProtocolException
-	{
-		byte[] bs = null;
-		try {
-			bs = s.getBytes(NetworkServerControlImpl.DEFAULT_ENCODING);
-		} catch (UnsupportedEncodingException e) {
-			//this should never happen
-			agent.agentError("Encoding " + NetworkServerControlImpl.DEFAULT_ENCODING + " not supported");
-		}
-		int len = bs.length;
-		if (len >= length)
-			writeBytes(bs, length);
-		else
-		{
-			writeBytes(bs);
-			padBytes(NetworkServerControlImpl.SPACE_CHAR, length-len);
+		ensureLength(maxEncodedLength(s));
+		CharBuffer input = CharBuffer.wrap(s);
+		CoderResult res = encoder.encode(input, buffer, true);
+		if (SanityManager.DEBUG) {
+			SanityManager.ASSERT(res == CoderResult.UNDERFLOW,
+								 "CharBuffer was not exhausted: res = " + res);
 		}
 	}
 

Modified: db/derby/code/trunk/java/drda/org/apache/derby/impl/drda/NetworkServerControlImpl.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/drda/org/apache/derby/impl/drda/NetworkServerControlImpl.java?view=diff&rev=557506&r1=557505&r2=557506
==============================================================================
--- db/derby/code/trunk/java/drda/org/apache/derby/impl/drda/NetworkServerControlImpl.java (original)
+++ db/derby/code/trunk/java/drda/org/apache/derby/impl/drda/NetworkServerControlImpl.java Thu Jul 19 00:11:19 2007
@@ -40,6 +40,7 @@
 import javax.net.ssl.SSLSocketFactory;
 import javax.net.ssl.SSLServerSocketFactory;
 import java.net.UnknownHostException;
+import java.nio.charset.Charset;
 import java.security.AccessController;
 import java.security.PrivilegedActionException;
 import java.security.PrivilegedExceptionAction;
@@ -169,6 +170,7 @@
 	protected final static int CCSIDSBC = 1208; //use UTF8
 	protected final static int CCSIDMBC = 1208; //use UTF8
 	protected final static String DEFAULT_ENCODING = "UTF8"; // use UTF8 for writing
+	final static Charset DEFAULT_CHARSET = Charset.forName(DEFAULT_ENCODING);
 	protected final static int DEFAULT_CCSID = 1208;
 	protected final static byte SPACE_CHAR = 32;