changeset 316:d479475888e3

Replace StringBuilder methods sb.delete(0, sb.length()) with faster sb.setLength(0). In MonetBlob create(hexString) optimized the conversion by eliminating the creation of many 2 char substrings. Small enhancements and optimizations and added/updated comments.
author Martin van Dinther <martin.van.dinther@monetdbsolutions.com>
date Thu, 29 Aug 2019 20:22:10 +0200 (2019-08-29)
parents 4793f9b80bb3
children b80d92601b4b
files src/main/java/nl/cwi/monetdb/client/JdbcClient.java src/main/java/nl/cwi/monetdb/jdbc/MonetBlob.java src/main/java/nl/cwi/monetdb/jdbc/MonetClob.java src/main/java/nl/cwi/monetdb/jdbc/MonetStatement.java src/main/java/nl/cwi/monetdb/mcl/parser/TupleLineParser.java tests/Test_Clargequery.java
diffstat 6 files changed, 51 insertions(+), 52 deletions(-) [+]
line wrap: on
line diff
--- a/src/main/java/nl/cwi/monetdb/client/JdbcClient.java
+++ b/src/main/java/nl/cwi/monetdb/client/JdbcClient.java
@@ -878,7 +878,7 @@ public final class JdbcClient {
 	 * @throws IOException if an IO exception occurs.
 	 */
 	public static void processBatch(final int batchSize) throws IOException {
-		final StringBuilder query = new StringBuilder();
+		final StringBuilder query = new StringBuilder(2048);
 		int i = 0;
 		try {
 			String curLine;
@@ -889,7 +889,7 @@ public final class JdbcClient {
 					// lousy check for end of statement, but in batch mode it
 					// is not very important to catch all end of statements...
 					stmt.addBatch(query.toString());
-					query.delete(0, query.length());
+					query.setLength(0);	// clear the buffer
 				} else {
 					query.append('\n');
 				}
--- a/src/main/java/nl/cwi/monetdb/jdbc/MonetBlob.java
+++ b/src/main/java/nl/cwi/monetdb/jdbc/MonetBlob.java
@@ -34,20 +34,20 @@ public final class MonetBlob implements 
 		buf = data;
 	}
 
-	final static MonetBlob create(final String in) {
+	static final MonetBlob create(final String hexString) {
 		// unpack the HEX (BLOB) notation to real bytes
-		final int len = in.length() / 2;
+		final int len = hexString.length() / 2;
 		final byte[] buf = new byte[len];
-		int offset;
 		for (int i = 0; i < len; i++) {
-			offset = 2 * i;
-			buf[i] = (byte)Integer.parseInt(in.substring(offset, offset + 2), 16);
+//	was		buf[i] = (byte)Integer.parseInt(hexString.substring(2 * i, (2 * i) + 2), 16);
+			buf[i] = (byte) ((Character.digit(hexString.charAt(2 * i), 16) << 4)
+					+ Character.digit(hexString.charAt((2 * i) +1), 16));
 		}
 		return new MonetBlob(buf);
 	}
 
 	/* internal utility method */
-	final private void checkBufIsNotNull() throws SQLException {
+	private final void checkBufIsNotNull() throws SQLException {
 		if (buf == null)
 			throw new SQLException("This MonetBlob has been freed", "M1M20");
 	}
@@ -106,7 +106,6 @@ public final class MonetBlob implements 
 		if (length < 0 || pos - 1 + length > buf.length) {
 			throw new SQLException("Invalid length value: " + length, "M1M05");
 		}
-
 		return new ByteArrayInputStream(buf, (int) pos - 1, (int) length);
 	}
 
@@ -196,14 +195,15 @@ public final class MonetBlob implements 
 		}
 		try {
 			final int patternLength = pattern.length;
-			final int bufLength = buf.length;
-			for (int i = (int)(start - 1); i < bufLength - patternLength; i++) {
+			final int maxPos = buf.length - patternLength;
+			for (int i = (int)(start - 1); i < maxPos; i++) {
 				int j;
 				for (j = 0; j < patternLength; j++) {
 					if (buf[i + j] != pattern[j])
 						break;
 				}
 				if (j == patternLength)
+					// found a match
 					return i;
 			}
 		} catch (IndexOutOfBoundsException e) {
@@ -248,11 +248,9 @@ public final class MonetBlob implements 
 	 */
 	@Override
 	public int setBytes(final long pos, final byte[] bytes) throws SQLException {
-		if (bytes == null) {
-			throw new SQLException("Missing bytes[] object", "M1M05");
-		}
-		// buf and input argument pos will be checked in method setBytes(long, byte{}, int, int)
-		return setBytes(pos, bytes, 1, bytes.length);
+		// buf and input arguments will be checked in method setBytes(long, byte{}, int, int)
+		final int len = (bytes != null) ? bytes.length : 0;
+		return setBytes(pos, bytes, 1, len);
 	}
 
 	/**
@@ -272,14 +270,14 @@ public final class MonetBlob implements 
 	 *         BLOB value or if pos is less than 1
 	 */
 	@Override
-	public int setBytes(final long pos, final byte[] bytes, final int offset, final int len)
+	public int setBytes(final long pos, final byte[] bytes, int offset, final int len)
 		throws SQLException
 	{
 		checkBufIsNotNull();
 		if (bytes == null) {
 			throw new SQLException("Missing bytes[] object", "M1M05");
 		}
-		if (pos < 1 || pos > Integer.MAX_VALUE) {
+		if (pos < 1 || pos > buf.length) {
 			throw new SQLException("Invalid pos value: " + pos, "M1M05");
 		}
 		if (len < 0 || pos + len > buf.length) {
@@ -290,9 +288,10 @@ public final class MonetBlob implements 
 		}
 
 		try {
+			offset--;
 			/* transactions? what are you talking about? */
 			for (int i = (int)pos; i < len; i++)
-				buf[i] = bytes[offset - 1 + i];
+				buf[i] = bytes[offset + i];
 		} catch (IndexOutOfBoundsException e) {
 			throw new SQLException(e.getMessage(), "M0M10");
 		}
@@ -314,10 +313,7 @@ public final class MonetBlob implements 
 			throw new SQLException("Invalid len value: " + len, "M1M05");
 		}
 		if (buf.length > len) {
-			final byte[] newbuf = new byte[(int)len];
-			for (int i = 0; i < len; i++)
-				newbuf[i] = buf[i];
-			buf = newbuf;
+			buf = java.util.Arrays.copyOf(buf, (int)len);
 		}
 	}
 }
--- a/src/main/java/nl/cwi/monetdb/jdbc/MonetClob.java
+++ b/src/main/java/nl/cwi/monetdb/jdbc/MonetClob.java
@@ -35,7 +35,7 @@ public final class MonetClob implements 
 	}
 
 	/* internal utility method */
-	final private void checkBufIsNotNull() throws SQLException {
+	private final void checkBufIsNotNull() throws SQLException {
 		if (buf == null)
 			throw new SQLException("This MonetClob has been freed", "M1M20");
 	}
@@ -98,10 +98,11 @@ public final class MonetClob implements 
 	 */
 	@Override
 	public Reader getCharacterStream(final long pos, final long length) throws SQLException {
-		// buf and input argument pos will be checked in method getSubString(long, int)
+		// as length will be casted to int, check it for too big value first
 		if (length < 0 || length > Integer.MAX_VALUE) {
 			throw new SQLException("Invalid length value: " + length, "M1M05");
 		}
+		// buf and input argument pos will be checked in method getSubString(long, int)
 		return new StringReader(getSubString(pos, (int)length));
 	}
 
@@ -164,10 +165,10 @@ public final class MonetClob implements 
 	 */
 	@Override
 	public long position(final Clob searchstr, final long start) throws SQLException {
-		// buf and input argument start will be checked in method position(String, long)
 		if (searchstr == null) {
 			throw new SQLException("Missing searchstr object", "M1M05");
 		}
+		// buf and input argument start will be checked in method position(String, long)
 		return position(searchstr.toString(), start);
 	}
 
@@ -250,11 +251,9 @@ public final class MonetClob implements 
 	 */
 	@Override
 	public int setString(final long pos, final String str) throws SQLException {
-		// buf will be checked in method setString(long, String, int, int)
-		if (str == null) {
-			throw new SQLException("Missing str object", "M1M05");
-		}
-		return setString(pos, str, 0, str.length());
+		// buf and input arguments will be checked in method setString(long, String, int, int)
+		final int len = (str != null) ? str.length() : 0;
+		return setString(pos, str, 0, len);
 	}
 
 	/**
@@ -287,12 +286,11 @@ public final class MonetClob implements 
 		if (len < 1 || (offset + len) > str.length()) {
 			throw new SQLException("Invalid len value: " + len, "M1M05");
 		}
-
-		final int ipos = (int) pos;
-		if ((ipos + len) > buf.capacity()) {
-			buf.ensureCapacity(ipos + len);
+		if (((int) pos + len) > buf.capacity()) {
+			// enlarge the buffer before filling it
+			buf.ensureCapacity((int) pos + len);
 		}
-		buf.replace(ipos - 1, ipos + len, str.substring(offset, (offset + len)));
+		buf.replace((int) pos - 1, (int) pos + len, str.substring(offset, (offset + len)));
 		return len;
 	}
 
@@ -310,8 +308,8 @@ public final class MonetClob implements 
 		if (len < 0 || len > buf.length()) {
 			throw new SQLException("Invalid len value: " + len, "M1M05");
 		}
-		buf.delete((int)len, buf.length());
-		// Attempts to reduce storage used for the character sequence.
+		buf.setLength((int)len);
+		// reduce storage used for the character sequence.
 		buf.trimToSize();
 	}
 
@@ -321,7 +319,9 @@ public final class MonetClob implements 
 	 *
 	 * @return the String this MonetClob wraps or empty string when this MonetClob was freed.
 	 */
-	final public String toString() {
-		return (buf != null) ? buf.toString() : "";
+	public String toString() {
+		if (buf == null)
+			return "";
+		return buf.toString();
 	}
 }
--- a/src/main/java/nl/cwi/monetdb/jdbc/MonetStatement.java
+++ b/src/main/java/nl/cwi/monetdb/jdbc/MonetStatement.java
@@ -228,7 +228,7 @@ public class MonetStatement
 					// send and receive
 					error |= internalBatch(tmpBatch, counts, offset, i + 1, e);
 					offset = i;
-					tmpBatch.delete(0, tmpBatch.length());
+					tmpBatch.setLength(0);	// clear the buffer
 					first = true;
 					continue;
 				}
@@ -236,7 +236,7 @@ public class MonetStatement
 					// send and receive
 					error |= internalBatch(tmpBatch, counts, offset, i + 1, e);
 					offset = i;
-					tmpBatch.delete(0, tmpBatch.length());
+					tmpBatch.setLength(0);	// clear the buffer
 					first = true;
 				}
 				if (first)
@@ -267,16 +267,16 @@ public class MonetStatement
 		throws BatchUpdateException
 	{
 		try {
-			boolean type = internalExecute(batch.toString());
+			boolean hasResultSet = internalExecute(batch.toString());
 			int count = -1;
 
-			if (!type)
+			if (!hasResultSet)
 				count = getUpdateCount();
 
 			do {
 				if (offset >= max)
 					throw new SQLException("Overflow: don't use multi statements when batching (" + max + ")", "M1M16");
-				if (type) {
+				if (hasResultSet) {
 					e.setNextException(
 						new SQLException("Batch query produced a ResultSet! " +
 							"Ignoring and setting update count to " +
@@ -286,7 +286,7 @@ public class MonetStatement
 					counts[offset] = count;
 				}
 				offset++;
-			} while ((type = getMoreResults()) || (count = getUpdateCount()) != -1);
+			} while ((hasResultSet = getMoreResults()) || (count = getUpdateCount()) != -1);
 		} catch (SQLException ex) {
 			e.setNextException(ex);
 			for (; offset < max; offset++) {
--- a/src/main/java/nl/cwi/monetdb/mcl/parser/TupleLineParser.java
+++ b/src/main/java/nl/cwi/monetdb/mcl/parser/TupleLineParser.java
@@ -58,7 +58,9 @@ public final class TupleLineParser exten
 			throw new MCLParseException("Expected a data row starting with [");
 
 		// It is a tuple. Extract separate fields by examining the string data char for char
-		final char[] chrLine = source.toCharArray();	// convert whole string to char[] to avoid overhead of source.charAt(i) calls TODO: measure the overhead
+		// convert whole string to char[] to avoid overhead of source.charAt(i) calls
+		// TODO: measure the source.charAt(i) overhead and whether it is faster to eliminate the source.toCharArray(); copy
+		final char[] chrLine = source.toCharArray();
 		boolean inString = false, escaped = false, fieldHasEscape = false;
 		final StringBuilder uesc = new StringBuilder(128);	// used for building field string value when an escape is present in the field value
 		int column = 0, cursor = 2;
@@ -102,8 +104,8 @@ public final class TupleLineParser exten
 						{
 							if (fieldHasEscape) {
 								// reuse the StringBuilder by cleaning it
-								uesc.delete(0, uesc.length());
-								// prevent capacity increasements
+								uesc.setLength(0);
+								// prevent multiple capacity increments during the append()'s in the inner loop
 								uesc.ensureCapacity(endpos - (cursor + 1));
 								// parse the field value (excluding the double quotes) and convert it to a string without any escape characters
 								for (int pos = cursor + 1; pos < endpos; pos++) {
@@ -141,7 +143,7 @@ public final class TupleLineParser exten
 													if (chr2 >= '0' && chr2 <= '7' && chr3 >= '0' && chr3 <= '7') {
 														// we got an octal number between \000 and \377
 														try {
-															uesc.append((char)(Integer.parseInt("" + chr + chr2 + chr3, 8)));
+															uesc.append((char)(Integer.parseInt(new String(chrLine, pos, 3), 8)));
 															pos += 2;
 														} catch (NumberFormatException e) {
 															// hmmm, this point should never be reached actually...
--- a/tests/Test_Clargequery.java
+++ b/tests/Test_Clargequery.java
@@ -34,7 +34,7 @@ public class Test_Clargequery {
 			"select 1;\n";
 
 		int size = 1000;
-		StringBuffer bigq = new StringBuffer(query.length() * size);
+		StringBuilder bigq = new StringBuilder(query.length() * size);
 		for (int i = 0; i < size; i++) {
 			bigq.append(query);
 		}
@@ -61,7 +61,8 @@ public class Test_Clargequery {
 			System.out.println("ABORTING TEST!!!");
 		}
 
-		if (rs != null) rs.close();
+		if (rs != null)
+			rs.close();
 
 		con1.close();
 	}