diff --git a/CHANGES.txt b/CHANGES.txt index ad7d70c9d2c..6bcd872403d 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -150,6 +150,8 @@ Optimizations 4. SOLR-354: Optimize removing all documents. Now when a delete by query of *:* is issued, the current index is removed. (yonik) + 5. SOLR-377: Speed up response writers. (yonik) + Bug Fixes 1. Make TextField respect sortMissingFirst and sortMissingLast fields. (J.J. Larrea via yonik) diff --git a/src/java/org/apache/solr/common/util/FastWriter.java b/src/java/org/apache/solr/common/util/FastWriter.java new file mode 100755 index 00000000000..45f06e4f2f6 --- /dev/null +++ b/src/java/org/apache/solr/common/util/FastWriter.java @@ -0,0 +1,128 @@ +/** + * 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.solr.common.util; + +import java.io.Writer; +import java.io.IOException; + +/** Single threaded BufferedWriter + * Internal Solr use only, subject to change. + */ +public class FastWriter extends Writer { + // use default BUFSIZE of BufferedWriter so if we wrap that + // it won't cause double buffering. + private static final int BUFSIZE = 8192; + private final Writer sink; + private final char[] buf; + private int pos; + + public FastWriter(Writer w) { + this(w, new char[BUFSIZE], 0); + } + + public FastWriter(Writer sink, char[] tempBuffer, int start) { + this.sink = sink; + this.buf = tempBuffer; + this.pos = start; + } + + public static FastWriter wrap(Writer sink) { + return (sink instanceof FastWriter) ? (FastWriter)sink : new FastWriter(sink); + } + + @Override + public void write(int c) throws IOException { + write((char)c); + } + + public void write(char c) throws IOException { + if (pos >= buf.length) { + sink.write(buf,0,pos); + pos=0; + } + buf[pos++] = (char)c; + } + + @Override + public FastWriter append(char c) throws IOException { + if (pos >= buf.length) { + sink.write(buf,0,pos); + pos=0; + } + buf[pos++] = (char)c; + return this; + } + + @Override + public void write(char cbuf[], int off, int len) throws IOException { + int space = buf.length - pos; + if (len < space) { + System.arraycopy(cbuf, off, buf, pos, len); + pos += len; + } else if (len"); + out.write('/'); + out.write('>'); } else { out.write('>'); escapeCharData(val,out); - out.write("'); } @@ -104,16 +106,19 @@ public class XML { for (int i=0; i"); + out.write('/'); + out.write('>'); } else { out.write('>'); out.write(val); - out.write("'); } @@ -126,16 +131,19 @@ public class XML { for (int i=0; i"); + out.write('/'); + out.write('>'); } else { out.write('>'); escapeCharData(val,out); - out.write("'); } @@ -143,41 +151,16 @@ public class XML { private static void escape(String str, Writer out, String[] escapes) throws IOException { - int start=0; - // "n" was used for counting the chars added to out... - // removed cause it wasn't really useful so far. - // int n=0; - - for (int i=start; i 127 - //out.write(str, start, i-start); - // n+=i-start; + String replacement = escapes[ch]; + if (replacement != null) { + out.write(replacement); + continue; } - out.write(subst); - // n+=subst.length(); - start=i+1; } + out.write(ch); } - if (start==0) { - out.write(str); - // n += str.length(); - } else if (start 127 - // out.write(str, start, str.length()-start); - // n += str.length()-start; - } - // return n; } } diff --git a/src/java/org/apache/solr/request/JSONResponseWriter.java b/src/java/org/apache/solr/request/JSONResponseWriter.java index f4797da7fba..7d88c6d8564 100644 --- a/src/java/org/apache/solr/request/JSONResponseWriter.java +++ b/src/java/org/apache/solr/request/JSONResponseWriter.java @@ -81,8 +81,9 @@ class JSONWriter extends TextResponseWriter { } writeNamedList(null, rsp.getValues()); if(wrapperFunction!=null) { - writer.write(")"); + writer.write(')'); } + writer.flushBuffer(); } protected void writeKey(String fname, boolean needsEscaping) throws IOException { @@ -496,36 +497,37 @@ class JSONWriter extends TextResponseWriter { escaped: quotation mark, reverse solidus, and the control characters (U+0000 through U+001F). */ - - StringBuilder sb = new StringBuilder(val.length()+8); - sb.append('"'); + writer.write('"'); for (int i=0; i '#' && ch != '\\') || ch==' ') { // fast path + writer.write(ch); + continue; + } switch(ch) { case '"': case '\\': - sb.append('\\'); - sb.append(ch); + writer.write('\\'); + writer.write(ch); break; - case '\r': sb.append('\\').append('r'); break; - case '\n': sb.append('\\').append('n'); break; - case '\t': sb.append('\\').append('t'); break; - case '\b': sb.append('\\').append('b'); break; - case '\f': sb.append('\\').append('f'); break; + case '\r': writer.write('\\'); writer.write('r'); break; + case '\n': writer.write('\\'); writer.write('n'); break; + case '\t': writer.write('\\'); writer.write('t'); break; + case '\b': writer.write('\\'); writer.write('b'); break; + case '\f': writer.write('\\'); writer.write('f'); break; // case '/': default: { if (ch <= 0x1F) { - unicodeEscape(sb,ch); + unicodeEscape(writer,ch); } else { - sb.append(ch); + writer.write(ch); } } } } - sb.append('"'); - writer.append(sb); + writer.write('"'); } else { writer.write('"'); writer.write(val); @@ -673,15 +675,14 @@ class JSONWriter extends TextResponseWriter { writeStr(name, val, false); } - protected static void unicodeEscape(Appendable sb, int ch) throws IOException { - String str = Integer.toHexString(ch & 0xffff); - switch (str.length()) { - case 1: sb.append("\\u000"); break; - case 2: sb.append("\\u00"); break; - case 3: sb.append("\\u0"); break; - default: sb.append("\\u"); break; - } - sb.append(str); + private static char[] hexdigits = {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'}; + protected static void unicodeEscape(Appendable out, int ch) throws IOException { + out.append('\\'); + out.append('u'); + out.append(hexdigits[(ch>>>12) ]); + out.append(hexdigits[(ch>>>8) & 0xf]); + out.append(hexdigits[(ch>>>4) & 0xf]); + out.append(hexdigits[(ch) & 0xf]); } } diff --git a/src/java/org/apache/solr/request/PythonResponseWriter.java b/src/java/org/apache/solr/request/PythonResponseWriter.java index 9b0345c7d99..433f4cdb7b0 100644 --- a/src/java/org/apache/solr/request/PythonResponseWriter.java +++ b/src/java/org/apache/solr/request/PythonResponseWriter.java @@ -95,7 +95,10 @@ class PythonWriter extends JSONWriter { } } - writer.write( needUnicode ? "u'" : "'"); + if (needUnicode) { + writer.write('u'); + } + writer.write('\''); writer.append(sb); writer.write('\''); } diff --git a/src/java/org/apache/solr/request/RubyResponseWriter.java b/src/java/org/apache/solr/request/RubyResponseWriter.java index 8864c49da55..7231eccd0ab 100644 --- a/src/java/org/apache/solr/request/RubyResponseWriter.java +++ b/src/java/org/apache/solr/request/RubyResponseWriter.java @@ -51,7 +51,8 @@ class RubyWriter extends JSONWriter { @Override protected void writeKey(String fname, boolean needsEscaping) throws IOException { writeStr(null, fname, needsEscaping); - writer.write("=>"); + writer.write('='); + writer.write('>'); } @Override @@ -63,16 +64,13 @@ class RubyWriter extends JSONWriter { // Also, there are very few escapes recognized in a single quoted string, so // only escape the backslash and single quote. writer.write('\''); - // it might be more efficient to use a stringbuilder or write substrings - // if writing chars to the stream is slow. if (needsEscaping) { for (int i=0; i