am 9500bd1f: Remove StaleDexCacheError.
* commit '9500bd1ffe150e8836ea649ea356c4d1f3a62018':
Remove StaleDexCacheError.
diff --git a/benchmarks/src/benchmarks/XmlSerializeBenchmark.java b/benchmarks/src/benchmarks/XmlSerializeBenchmark.java
new file mode 100644
index 0000000..7319490
--- /dev/null
+++ b/benchmarks/src/benchmarks/XmlSerializeBenchmark.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2015 Google Inc.
+ *
+ * Licensed 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 benchmarks;
+
+import com.google.caliper.Param;
+import com.google.caliper.Runner;
+import com.google.caliper.SimpleBenchmark;
+
+import org.xmlpull.v1.*;
+
+import java.io.CharArrayWriter;
+import java.io.IOException;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.util.Random;
+
+
+public class XmlSerializeBenchmark extends SimpleBenchmark {
+
+ @Param( {"0.99 0.7 0.7 0.7 0.7 0.7",
+ "0.999 0.3 0.3 0.95 0.9 0.9"})
+ String datasetAsString;
+
+ @Param( { "854328", "312547"} )
+ int seed;
+
+ double[] dataset;
+ private Constructor<? extends XmlSerializer> kxmlConstructor;
+ private Constructor<? extends XmlSerializer> fastConstructor;
+
+ private void serializeRandomXml(Constructor<? extends XmlSerializer> ctor, long seed)
+ throws Exception {
+ double contChance = dataset[0];
+ double levelUpChance = dataset[1];
+ double levelDownChance = dataset[2];
+ double attributeChance = dataset[3];
+ double writeChance1 = dataset[4];
+ double writeChance2 = dataset[5];
+
+ XmlSerializer serializer = (XmlSerializer) ctor.newInstance();
+
+ CharArrayWriter w = new CharArrayWriter();
+ serializer.setOutput(w);
+ int level = 0;
+ Random r = new Random(seed);
+ char[] toWrite = {'a','b','c','d','s','z'};
+ serializer.startDocument("UTF-8", true);
+ while(r.nextDouble() < contChance) {
+ while(level > 0 && r.nextDouble() < levelUpChance) {
+ serializer.endTag("aaaaaa", "bbbbbb");
+ level--;
+ }
+ while(r.nextDouble() < levelDownChance) {
+ serializer.startTag("aaaaaa", "bbbbbb");
+ level++;
+ }
+ serializer.startTag("aaaaaa", "bbbbbb");
+ level++;
+ while(r.nextDouble() < attributeChance) {
+ serializer.attribute("aaaaaa", "cccccc", "dddddd");
+ }
+ serializer.endTag("aaaaaa", "bbbbbb");
+ level--;
+ while(r.nextDouble() < writeChance1)
+ serializer.text(toWrite, 0, 5);
+ while(r.nextDouble() < writeChance2)
+ serializer.text("Textxtsxtxtxt ");
+ }
+ serializer.endDocument();
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ protected void setUp() throws Exception {
+ kxmlConstructor = (Constructor) Class.forName("org.kxml2.io.KXmlSerializer")
+ .getConstructor();
+ fastConstructor = (Constructor) Class.forName("com.android.internal.util.FastXmlSerializer")
+ .getConstructor();
+ String[] splitted = datasetAsString.split(" ");
+ dataset = new double[splitted.length];
+ for (int i = 0; i < splitted.length; i++) {
+ dataset[i] = Double.valueOf(splitted[i]);
+ }
+ }
+
+ private void internalTimeSerializer(Constructor<? extends XmlSerializer> ctor, int reps)
+ throws Exception {
+ for (int i = 0; i < reps; i++) {
+ serializeRandomXml(ctor, seed);
+ }
+ }
+
+ public void timeKxml(int reps) throws Exception {
+ internalTimeSerializer(kxmlConstructor, reps);
+ }
+
+ public void timeFast(int reps) throws Exception {
+ internalTimeSerializer(fastConstructor, reps);
+ }
+
+ public static void main(String[] args) {
+ Runner.main(XmlSerializeBenchmark.class, args);
+ }
+}
diff --git a/dalvik/src/main/java/dalvik/system/DexPathList.java b/dalvik/src/main/java/dalvik/system/DexPathList.java
index b1ff1c8..4bfc808 100644
--- a/dalvik/src/main/java/dalvik/system/DexPathList.java
+++ b/dalvik/src/main/java/dalvik/system/DexPathList.java
@@ -19,14 +19,22 @@
import android.system.ErrnoException;
import android.system.StructStat;
import java.io.File;
+import java.io.FilterInputStream;
import java.io.IOException;
+import java.io.InputStream;
+import java.net.JarURLConnection;
import java.net.MalformedURLException;
+import java.net.URI;
import java.net.URL;
+import java.net.URLConnection;
+import java.net.URLStreamHandler;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;
+import java.util.jar.JarFile;
+import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import libcore.io.IoUtils;
import libcore.io.Libcore;
@@ -410,6 +418,7 @@
private final DexFile dexFile;
private ZipFile zipFile;
+ private URLStreamHandler urlHandler;
private boolean initialized;
public Element(File file, boolean isDirectory, File zip, DexFile dexFile) {
@@ -442,6 +451,7 @@
try {
zipFile = new ZipFile(zip);
+ urlHandler = new ElementURLStreamHandler(zipFile);
} catch (IOException ioe) {
/*
* Note: ZipException (a subclass of IOException)
@@ -481,16 +491,114 @@
try {
/*
- * File.toURL() is compliant with RFC 1738 in
+ * File.toURI() is compliant with RFC 1738 in
* always creating absolute path names. If we
* construct the URL by concatenating strings, we
* might end up with illegal URLs for relative
* names.
*/
- return new URL("jar:" + file.toURL() + "!/" + name);
+ URI fileUri = file.toURI();
+ return new URL("jar", null, -1, fileUri.toString() + "!/" + name, urlHandler);
} catch (MalformedURLException ex) {
throw new RuntimeException(ex);
}
}
+
+ /**
+ * URLStreamHandler for an Element. Avoids the need to open a .jar file again to read
+ * resources.
+ */
+ private static class ElementURLStreamHandler extends URLStreamHandler {
+
+ private final ZipFile zipFile;
+
+ public ElementURLStreamHandler(ZipFile zipFile) {
+ this.zipFile = zipFile;
+ }
+
+ @Override
+ protected URLConnection openConnection(URL url) throws IOException {
+ return new ElementJarURLConnection(url, zipFile);
+ }
+ }
+
+ /**
+ * A JarURLConnection that is backed by a ZipFile held open by an {@link Element}. For
+ * backwards compatibility it extends JarURLConnection even though it's not actually backed
+ * by a {@link JarFile}.
+ */
+ private static class ElementJarURLConnection extends JarURLConnection {
+
+ private final ZipFile zipFile;
+ private final ZipEntry zipEntry;
+
+ private InputStream jarInput;
+ private boolean closed;
+ private JarFile jarFile;
+
+ public ElementJarURLConnection(URL url, ZipFile zipFile) throws MalformedURLException {
+ super(url);
+ this.zipFile = zipFile;
+ this.zipEntry = zipFile.getEntry(getEntryName());
+ if (zipEntry == null) {
+ throw new MalformedURLException(
+ "URL does not correspond to an entry in the zip file. URL=" + url
+ + ", zipfile=" + zipFile.getName());
+ }
+ }
+
+ @Override
+ public void connect() {
+ connected = true;
+ }
+
+ @Override
+ public JarFile getJarFile() throws IOException {
+ // This is expensive because we only pretend that we wrap a JarFile.
+ if (jarFile == null) {
+ jarFile = new JarFile(zipFile.getName());
+ }
+ return jarFile;
+ }
+
+ @Override
+ public InputStream getInputStream() throws IOException {
+ if (closed) {
+ throw new IllegalStateException("JarURLConnection InputStream has been closed");
+ }
+ connect();
+ if (jarInput != null) {
+ return jarInput;
+ }
+ return jarInput = new FilterInputStream(zipFile.getInputStream(zipEntry)) {
+ @Override
+ public void close() throws IOException {
+ super.close();
+ closed = true;
+ }
+ };
+ }
+
+ /**
+ * Returns the content type of the entry based on the name of the entry. Returns
+ * non-null results ("content/unknown" for unknown types).
+ *
+ * @return the content type
+ */
+ @Override
+ public String getContentType() {
+ String cType = guessContentTypeFromName(getEntryName());
+ if (cType == null) {
+ cType = "content/unknown";
+ }
+ return cType;
+ }
+
+ @Override
+ public int getContentLength() {
+ connect();
+ return (int) zipEntry.getSize();
+ }
+ }
}
}
diff --git a/expectations/knownfailures.txt b/expectations/knownfailures.txt
index c23deff..a3e9197 100644
--- a/expectations/knownfailures.txt
+++ b/expectations/knownfailures.txt
@@ -1462,6 +1462,14 @@
]
},
{
+ description: "OkHttp tests that fail on Wear devices due to a lack of memory",
+ bug: 20055487,
+ names: [
+ "com.squareup.okhttp.internal.spdy.Http20Draft09Test#tooLargeDataFrame",
+ "com.squareup.okhttp.internal.spdy.Spdy3Test#tooLargeDataFrame"
+ ]
+},
+{
description: "libcore.java.text.DecimalFormatSymbolsTest#test_getInstance_unknown_or_invalid_locale assumes fallback to locale other than en_US_POSIX.",
bug: 17374604,
names: [
@@ -1501,5 +1509,13 @@
"libcore.java.util.zip.Zip64FileTest#testZip64Support_totalLargerThan4G",
"libcore.java.util.zip.Zip64FileTest#testZip64Support_hugeEntry"
]
+},
+{
+ description: "ICU bug in formatting of dates that span month boundaries",
+ result: EXEC_FAILED,
+ bug: 20708022,
+ names: [
+ "libcore.icu.DateIntervalFormatTest#testEndOfDayOnLastDayOfMonth"
+ ]
}
]
diff --git a/luni/src/test/java/libcore/icu/DateIntervalFormatTest.java b/luni/src/test/java/libcore/icu/DateIntervalFormatTest.java
index 7adad72..6e0bce6 100644
--- a/luni/src/test/java/libcore/icu/DateIntervalFormatTest.java
+++ b/luni/src/test/java/libcore/icu/DateIntervalFormatTest.java
@@ -423,4 +423,13 @@
assertEquals("11 AM – 1 PM", formatDateRange(l, utc, 11*HOUR, 13*HOUR, flags));
assertEquals("2 – 3 PM", formatDateRange(l, utc, 14*HOUR, 15*HOUR, flags));
}
+
+ // http://b/20708022
+ public void testEndOfDayOnLastDayOfMonth() throws Exception {
+ final ULocale locale = new ULocale("en");
+ final TimeZone timeZone = TimeZone.getTimeZone("UTC");
+
+ assertEquals("April 30, 11:00 PM – May 1, 12:00 AM", formatDateRange(locale, timeZone,
+ 1430434800000L, 1430438400000L, FORMAT_SHOW_TIME));
+ }
}
diff --git a/luni/src/test/java/libcore/xml/PullParserTest.java b/luni/src/test/java/libcore/xml/PullParserTest.java
index c59b358..b204c88 100644
--- a/luni/src/test/java/libcore/xml/PullParserTest.java
+++ b/luni/src/test/java/libcore/xml/PullParserTest.java
@@ -766,6 +766,16 @@
assertRelaxedParseFailure("<!DOCTYPE foo [<!ELEMENT foo EMPTY"); // EOF in read('>')
}
+ public void testWhitespacesAfterDOCTYPE() throws Exception {
+ XmlPullParser parser = newPullParser();
+ String test = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
+ "<!DOCTYPE root [\n" +
+ "<!ENTITY dummy \"dummy\">\n" +
+ "]> \n" +
+ "<root></root>";
+ assertParseSuccess(test, parser);
+ }
+
private void assertParseFailure(String xml) throws Exception {
XmlPullParser parser = newPullParser();
assertParseFailure(xml, parser);
@@ -787,6 +797,12 @@
}
}
+ private void assertParseSuccess(String xml, XmlPullParser parser) throws Exception {
+ parser.setInput(new StringReader(xml));
+ while (parser.next() != XmlPullParser.END_DOCUMENT) {
+ }
+ }
+
/**
* Creates a new pull parser.
*/
diff --git a/xml/src/main/java/org/kxml2/io/KXmlParser.java b/xml/src/main/java/org/kxml2/io/KXmlParser.java
index a90db3b..2e32bf1 100644
--- a/xml/src/main/java/org/kxml2/io/KXmlParser.java
+++ b/xml/src/main/java/org/kxml2/io/KXmlParser.java
@@ -604,6 +604,7 @@
}
read('>');
+ skip();
}
/**
diff --git a/xml/src/main/java/org/kxml2/io/KXmlSerializer.java b/xml/src/main/java/org/kxml2/io/KXmlSerializer.java
index bfdeece..399462d 100644
--- a/xml/src/main/java/org/kxml2/io/KXmlSerializer.java
+++ b/xml/src/main/java/org/kxml2/io/KXmlSerializer.java
@@ -27,17 +27,13 @@
public class KXmlSerializer implements XmlSerializer {
+ private static final int BUFFER_LEN = 8192;
+ private final char[] mText = new char[BUFFER_LEN];
+ private int mPos;
+
// static final String UNDEFINED = ":";
- // BEGIN android-added
- /** size (in characters) for the write buffer */
- private static final int WRITE_BUFFER_SIZE = 500;
- // END android-added
-
- // BEGIN android-changed
- // (Guarantee that the writer is always buffered.)
- private BufferedWriter writer;
- // END android-changed
+ private Writer writer;
private boolean pending;
private int auto;
@@ -52,6 +48,41 @@
private boolean unicode;
private String encoding;
+ private void append(char c) throws IOException {
+ if (mPos >= BUFFER_LEN) {
+ flushBuffer();
+ }
+ mText[mPos++] = c;
+ }
+
+ private void append(String str, int i, int length) throws IOException {
+ while (length > 0) {
+ if (mPos == BUFFER_LEN) {
+ flushBuffer();
+ }
+ int batch = BUFFER_LEN - mPos;
+ if (batch > length) {
+ batch = length;
+ }
+ str.getChars(i, i + batch, mText, mPos);
+ i += batch;
+ length -= batch;
+ mPos += batch;
+ }
+ }
+
+ private void append(String str) throws IOException {
+ append(str, 0, str.length());
+ }
+
+ private final void flushBuffer() throws IOException {
+ if(mPos > 0) {
+ writer.write(mText, 0, mPos);
+ writer.flush();
+ mPos = 0;
+ }
+ }
+
private final void check(boolean close) throws IOException {
if (!pending)
return;
@@ -67,17 +98,16 @@
indent[depth] = indent[depth - 1];
for (int i = nspCounts[depth - 1]; i < nspCounts[depth]; i++) {
- writer.write(' ');
- writer.write("xmlns");
+ append(" xmlns");
if (!nspStack[i * 2].isEmpty()) {
- writer.write(':');
- writer.write(nspStack[i * 2]);
+ append(':');
+ append(nspStack[i * 2]);
}
else if (getNamespace().isEmpty() && !nspStack[i * 2 + 1].isEmpty())
throw new IllegalStateException("Cannot set default namespace for elements in no namespace");
- writer.write("=\"");
+ append("=\"");
writeEscaped(nspStack[i * 2 + 1], '"');
- writer.write('"');
+ append('"');
}
if (nspCounts.length <= depth + 1) {
@@ -89,7 +119,11 @@
nspCounts[depth + 1] = nspCounts[depth];
// nspCounts[depth + 2] = nspCounts[depth];
- writer.write(close ? " />" : ">");
+ if (close) {
+ append(" />");
+ } else {
+ append('>');
+ }
}
private final void writeEscaped(String s, int quot) throws IOException {
@@ -100,22 +134,22 @@
case '\r':
case '\t':
if(quot == -1)
- writer.write(c);
+ append(c);
else
- writer.write("&#"+((int) c)+';');
+ append("&#"+((int) c)+';');
break;
case '&' :
- writer.write("&");
+ append("&");
break;
case '>' :
- writer.write(">");
+ append(">");
break;
case '<' :
- writer.write("<");
+ append("<");
break;
default:
if (c == quot) {
- writer.write(c == '"' ? """ : "'");
+ append(c == '"' ? """ : "'");
break;
}
// BEGIN android-changed: refuse to output invalid characters
@@ -128,9 +162,9 @@
boolean allowedInXml = (c >= 0x20 && c <= 0xd7ff) || (c >= 0xe000 && c <= 0xfffd);
if (allowedInXml) {
if (unicode || c < 127) {
- writer.write(c);
+ append(c);
} else {
- writer.write("&#" + ((int) c) + ";");
+ append("&#" + ((int) c) + ";");
}
} else if (Character.isHighSurrogate(c) && i < s.length() - 1) {
writeSurrogate(c, s.charAt(i + 1));
@@ -157,9 +191,9 @@
}*/
public void docdecl(String dd) throws IOException {
- writer.write("<!DOCTYPE");
- writer.write(dd);
- writer.write(">");
+ append("<!DOCTYPE");
+ append(dd);
+ append('>');
}
public void endDocument() throws IOException {
@@ -171,9 +205,9 @@
public void entityRef(String name) throws IOException {
check(false);
- writer.write('&');
- writer.write(name);
- writer.write(';');
+ append('&');
+ append(name);
+ append(';');
}
public boolean getFeature(String name) {
@@ -301,14 +335,7 @@
}
public void setOutput(Writer writer) {
- // BEGIN android-changed
- // Guarantee that the writer is always buffered.
- if (writer instanceof BufferedWriter) {
- this.writer = (BufferedWriter) writer;
- } else {
- this.writer = new BufferedWriter(writer, WRITE_BUFFER_SIZE);
- }
- // END android-changed
+ this.writer = writer;
// elementStack = new String[12]; //nsp/prefix/name
//nspCounts = new int[4];
@@ -343,7 +370,7 @@
}
public void startDocument(String encoding, Boolean standalone) throws IOException {
- writer.write("<?xml version='1.0' ");
+ append("<?xml version='1.0' ");
if (encoding != null) {
this.encoding = encoding;
@@ -353,18 +380,17 @@
}
if (this.encoding != null) {
- writer.write("encoding='");
- writer.write(this.encoding);
- writer.write("' ");
+ append("encoding='");
+ append(this.encoding);
+ append("' ");
}
if (standalone != null) {
- writer.write("standalone='");
- writer.write(
- standalone.booleanValue() ? "yes" : "no");
- writer.write("' ");
+ append("standalone='");
+ append(standalone.booleanValue() ? "yes" : "no");
+ append("' ");
}
- writer.write("?>");
+ append("?>");
}
public XmlSerializer startTag(String namespace, String name)
@@ -375,9 +401,9 @@
// namespace = "";
if (indent[depth]) {
- writer.write("\r\n");
+ append("\r\n");
for (int i = 0; i < depth; i++)
- writer.write(" ");
+ append(" ");
}
int esp = depth * 3;
@@ -407,13 +433,13 @@
elementStack[esp++] = prefix;
elementStack[esp] = name;
- writer.write('<');
+ append('<');
if (!prefix.isEmpty()) {
- writer.write(prefix);
- writer.write(':');
+ append(prefix);
+ append(':');
}
- writer.write(name);
+ append(name);
pending = true;
@@ -457,24 +483,24 @@
}
*/
- writer.write(' ');
+ append(' ');
if (!prefix.isEmpty()) {
- writer.write(prefix);
- writer.write(':');
+ append(prefix);
+ append(':');
}
- writer.write(name);
- writer.write('=');
+ append(name);
+ append('=');
char q = value.indexOf('"') == -1 ? '"' : '\'';
- writer.write(q);
+ append(q);
writeEscaped(value, q);
- writer.write(q);
+ append(q);
return this;
}
public void flush() throws IOException {
check(false);
- writer.flush();
+ flushBuffer();
}
/*
public void close() throws IOException {
@@ -503,19 +529,19 @@
}
else {
if (indent[depth + 1]) {
- writer.write("\r\n");
+ append("\r\n");
for (int i = 0; i < depth; i++)
- writer.write(" ");
+ append(" ");
}
- writer.write("</");
+ append("</");
String prefix = elementStack[depth * 3 + 1];
if (!prefix.isEmpty()) {
- writer.write(prefix);
- writer.write(':');
+ append(prefix);
+ append(':');
}
- writer.write(name);
- writer.write('>');
+ append(name);
+ append('>');
}
nspCounts[depth + 1] = nspCounts[depth];
@@ -552,24 +578,24 @@
// BEGIN android-changed: ]]> is not allowed within a CDATA,
// so break and start a new one when necessary.
data = data.replace("]]>", "]]]]><![CDATA[>");
- writer.write("<![CDATA[");
+ append("<![CDATA[");
for (int i = 0; i < data.length(); ++i) {
char ch = data.charAt(i);
boolean allowedInCdata = (ch >= 0x20 && ch <= 0xd7ff) ||
(ch == '\t' || ch == '\n' || ch == '\r') ||
(ch >= 0xe000 && ch <= 0xfffd);
if (allowedInCdata) {
- writer.write(ch);
+ append(ch);
} else if (Character.isHighSurrogate(ch) && i < data.length() - 1) {
// Character entities aren't valid in CDATA, so break out for this.
- writer.write("]]>");
+ append("]]>");
writeSurrogate(ch, data.charAt(++i));
- writer.write("<![CDATA[");
+ append("<![CDATA[");
} else {
reportInvalidCharacter(ch);
}
}
- writer.write("]]>");
+ append("]]>");
// END android-changed
}
@@ -583,22 +609,22 @@
// seems likely to upset anything expecting modified UTF-8 rather than "real" UTF-8. It seems more
// conservative in a Java environment to use an entity reference instead.
int codePoint = Character.toCodePoint(high, low);
- writer.write("&#" + codePoint + ";");
+ append("&#" + codePoint + ";");
}
// END android-added
public void comment(String comment) throws IOException {
check(false);
- writer.write("<!--");
- writer.write(comment);
- writer.write("-->");
+ append("<!--");
+ append(comment);
+ append("-->");
}
public void processingInstruction(String pi)
throws IOException {
check(false);
- writer.write("<?");
- writer.write(pi);
- writer.write("?>");
+ append("<?");
+ append(pi);
+ append("?>");
}
}