1 /* 2 * Copyright (C) 2011 Google Inc. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package benchmarks.regression; 18 19 import com.google.caliper.BeforeExperiment; 20 import com.google.caliper.Param; 21 import java.io.IOException; 22 import java.io.InputStream; 23 import java.io.InputStreamReader; 24 import java.io.Reader; 25 import java.io.StringReader; 26 import java.io.StringWriter; 27 import javax.xml.parsers.DocumentBuilderFactory; 28 import javax.xml.parsers.SAXParserFactory; 29 import org.json.JSONArray; 30 import org.json.JSONObject; 31 import org.xml.sax.InputSource; 32 import org.xml.sax.helpers.DefaultHandler; 33 import org.xmlpull.v1.XmlPullParser; 34 35 /** 36 * Measure throughput of various parsers. 37 * 38 * <p>This benchmark requires that ParseBenchmarkData.zip is on the classpath. 39 * That file contains Twitter feed data, which is representative of what 40 * applications will be parsing. 41 */ 42 public final class ParseBenchmark { 43 44 @Param Document document; 45 @Param Api api; 46 47 private enum Document { 48 TWEETS, 49 READER_SHORT, 50 READER_LONG 51 } 52 53 private enum Api { 54 ANDROID_STREAM("json") { 55 @Override Parser newParser() { 56 return new AndroidStreamParser(); 57 } 58 }, 59 ORG_JSON("json") { 60 @Override Parser newParser() { 61 return new OrgJsonParser(); 62 } 63 }, 64 XML_PULL("xml") { 65 @Override Parser newParser() { 66 return new GeneralXmlPullParser(); 67 } 68 }, 69 XML_DOM("xml") { 70 @Override Parser newParser() { 71 return new XmlDomParser(); 72 } 73 }, 74 XML_SAX("xml") { 75 @Override Parser newParser() { 76 return new XmlSaxParser(); 77 } 78 }; 79 80 final String extension; 81 82 private Api(String extension) { 83 this.extension = extension; 84 } 85 86 abstract Parser newParser(); 87 } 88 89 private String text; 90 private Parser parser; 91 92 @BeforeExperiment 93 protected void setUp() throws Exception { 94 text = resourceToString("/" + document.name() + "." + api.extension); 95 parser = api.newParser(); 96 } 97 98 public void timeParse(int reps) throws Exception { 99 for (int i = 0; i < reps; i++) { 100 parser.parse(text); 101 } 102 } 103 104 private static String resourceToString(String path) throws Exception { 105 InputStream in = ParseBenchmark.class.getResourceAsStream(path); 106 if (in == null) { 107 throw new IllegalArgumentException("No such file: " + path); 108 } 109 110 Reader reader = new InputStreamReader(in, "UTF-8"); 111 char[] buffer = new char[8192]; 112 StringWriter writer = new StringWriter(); 113 int count; 114 while ((count = reader.read(buffer)) != -1) { 115 writer.write(buffer, 0, count); 116 } 117 reader.close(); 118 return writer.toString(); 119 } 120 121 interface Parser { 122 void parse(String data) throws Exception; 123 } 124 125 private static class AndroidStreamParser implements Parser { 126 @Override public void parse(String data) throws Exception { 127 android.util.JsonReader jsonReader 128 = new android.util.JsonReader(new StringReader(data)); 129 readToken(jsonReader); 130 jsonReader.close(); 131 } 132 133 public void readObject(android.util.JsonReader reader) throws IOException { 134 reader.beginObject(); 135 while (reader.hasNext()) { 136 reader.nextName(); 137 readToken(reader); 138 } 139 reader.endObject(); 140 } 141 142 public void readArray(android.util.JsonReader reader) throws IOException { 143 reader.beginArray(); 144 while (reader.hasNext()) { 145 readToken(reader); 146 } 147 reader.endArray(); 148 } 149 150 private void readToken(android.util.JsonReader reader) throws IOException { 151 switch (reader.peek()) { 152 case BEGIN_ARRAY: 153 readArray(reader); 154 break; 155 case BEGIN_OBJECT: 156 readObject(reader); 157 break; 158 case BOOLEAN: 159 reader.nextBoolean(); 160 break; 161 case NULL: 162 reader.nextNull(); 163 break; 164 case NUMBER: 165 reader.nextLong(); 166 break; 167 case STRING: 168 reader.nextString(); 169 break; 170 default: 171 throw new IllegalArgumentException("Unexpected token" + reader.peek()); 172 } 173 } 174 } 175 176 private static class OrgJsonParser implements Parser { 177 @Override public void parse(String data) throws Exception { 178 if (data.startsWith("[")) { 179 new JSONArray(data); 180 } else if (data.startsWith("{")) { 181 new JSONObject(data); 182 } else { 183 throw new IllegalArgumentException(); 184 } 185 } 186 } 187 188 private static class GeneralXmlPullParser implements Parser { 189 @Override public void parse(String data) throws Exception { 190 XmlPullParser xmlParser = android.util.Xml.newPullParser(); 191 xmlParser.setInput(new StringReader(data)); 192 xmlParser.nextTag(); 193 while (xmlParser.next() != XmlPullParser.END_DOCUMENT) { 194 xmlParser.getName(); 195 xmlParser.getText(); 196 } 197 } 198 } 199 200 private static class XmlDomParser implements Parser { 201 @Override public void parse(String data) throws Exception { 202 DocumentBuilderFactory.newInstance().newDocumentBuilder() 203 .parse(new InputSource(new StringReader(data))); 204 } 205 } 206 207 private static class XmlSaxParser implements Parser { 208 @Override public void parse(String data) throws Exception { 209 SAXParserFactory.newInstance().newSAXParser().parse( 210 new InputSource(new StringReader(data)), new DefaultHandler()); 211 } 212 } 213 } 214