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("");
+ out.write('<');
+ out.write('/');
out.write(tag);
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("");
+ out.write('<');
+ out.write('/');
out.write(tag);
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("");
+ out.write('<');
+ out.write('/');
out.write(tag);
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