summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author TreeHugger Robot <treehugger-gerrit@google.com> 2022-10-25 00:23:38 +0000
committer Android (Google) Code Review <android-gerrit@google.com> 2022-10-25 00:23:38 +0000
commitf8a4152cd96d92e1511e6a1d1724fa243d66a12c (patch)
tree1382e1d9c8101e2d086dcfc0c8fe923f82c3c21c
parente363a3fea1791891bc8266f9596a53aa342b17fd (diff)
parentb90e6818c32504d925ee2d95252a7d212f6e7639 (diff)
Merge "Extract four byte sequence modified UTF-8 into separate classes."
-rw-r--r--apct-tests/perftests/core/src/com/android/internal/util/FastDataPerfTest.java8
-rw-r--r--core/java/android/net/netstats/NetworkStatsDataMigrationUtils.java4
-rw-r--r--core/java/android/util/Xml.java7
-rw-r--r--core/java/com/android/internal/util/ArtBinaryXmlPullParser.java36
-rw-r--r--core/java/com/android/internal/util/ArtBinaryXmlSerializer.java37
-rw-r--r--core/java/com/android/internal/util/ArtFastDataInput.java92
-rw-r--r--core/java/com/android/internal/util/ArtFastDataOutput.java101
-rw-r--r--core/java/com/android/internal/util/BinaryXmlPullParser.java9
-rw-r--r--core/java/com/android/internal/util/BinaryXmlSerializer.java9
-rw-r--r--core/java/com/android/internal/util/FastDataInput.java92
-rw-r--r--core/java/com/android/internal/util/FastDataOutput.java102
-rw-r--r--core/tests/coretests/src/com/android/internal/util/FastDataTest.java63
12 files changed, 358 insertions, 202 deletions
diff --git a/apct-tests/perftests/core/src/com/android/internal/util/FastDataPerfTest.java b/apct-tests/perftests/core/src/com/android/internal/util/FastDataPerfTest.java
index 76656bd67afa..b43ec10e1fc5 100644
--- a/apct-tests/perftests/core/src/com/android/internal/util/FastDataPerfTest.java
+++ b/apct-tests/perftests/core/src/com/android/internal/util/FastDataPerfTest.java
@@ -68,7 +68,7 @@ public class FastDataPerfTest {
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
while (state.keepRunning()) {
os.reset();
- final FastDataOutput out = FastDataOutput.obtainUsing4ByteSequences(os);
+ final FastDataOutput out = ArtFastDataOutput.obtain(os);
try {
doWrite(out);
out.flush();
@@ -84,7 +84,7 @@ public class FastDataPerfTest {
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
while (state.keepRunning()) {
os.reset();
- final FastDataOutput out = FastDataOutput.obtainUsing3ByteSequences(os);
+ final FastDataOutput out = FastDataOutput.obtain(os);
try {
doWrite(out);
out.flush();
@@ -116,7 +116,7 @@ public class FastDataPerfTest {
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
while (state.keepRunning()) {
is.reset();
- final FastDataInput in = FastDataInput.obtainUsing4ByteSequences(is);
+ final FastDataInput in = ArtFastDataInput.obtain(is);
try {
doRead(in);
} finally {
@@ -131,7 +131,7 @@ public class FastDataPerfTest {
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
while (state.keepRunning()) {
is.reset();
- final FastDataInput in = FastDataInput.obtainUsing3ByteSequences(is);
+ final FastDataInput in = FastDataInput.obtain(is);
try {
doRead(in);
} finally {
diff --git a/core/java/android/net/netstats/NetworkStatsDataMigrationUtils.java b/core/java/android/net/netstats/NetworkStatsDataMigrationUtils.java
index 76ee097c8c93..f8e25584c419 100644
--- a/core/java/android/net/netstats/NetworkStatsDataMigrationUtils.java
+++ b/core/java/android/net/netstats/NetworkStatsDataMigrationUtils.java
@@ -37,7 +37,7 @@ import android.os.Environment;
import android.util.AtomicFile;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.FastDataInput;
+import com.android.internal.util.ArtFastDataInput;
import libcore.io.IoUtils;
@@ -254,7 +254,7 @@ public class NetworkStatsDataMigrationUtils {
private static void readPlatformCollection(@NonNull NetworkStatsCollection.Builder builder,
@NonNull File file) throws IOException {
final FileInputStream is = new FileInputStream(file);
- final FastDataInput dataIn = new FastDataInput(is, BUFFER_SIZE);
+ final ArtFastDataInput dataIn = new ArtFastDataInput(is, BUFFER_SIZE);
try {
readPlatformCollection(builder, dataIn);
} finally {
diff --git a/core/java/android/util/Xml.java b/core/java/android/util/Xml.java
index 38decf9951a7..242806db5344 100644
--- a/core/java/android/util/Xml.java
+++ b/core/java/android/util/Xml.java
@@ -22,7 +22,8 @@ import android.os.SystemProperties;
import android.system.ErrnoException;
import android.system.Os;
-import com.android.internal.util.BinaryXmlPullParser;
+import com.android.internal.util.ArtBinaryXmlPullParser;
+import com.android.internal.util.ArtBinaryXmlSerializer;
import com.android.internal.util.BinaryXmlSerializer;
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.XmlUtils;
@@ -146,7 +147,7 @@ public class Xml {
* @hide
*/
public static @NonNull TypedXmlPullParser newBinaryPullParser() {
- return new BinaryXmlPullParser();
+ return new ArtBinaryXmlPullParser();
}
/**
@@ -225,7 +226,7 @@ public class Xml {
* @hide
*/
public static @NonNull TypedXmlSerializer newBinarySerializer() {
- return new BinaryXmlSerializer();
+ return new ArtBinaryXmlSerializer();
}
/**
diff --git a/core/java/com/android/internal/util/ArtBinaryXmlPullParser.java b/core/java/com/android/internal/util/ArtBinaryXmlPullParser.java
new file mode 100644
index 000000000000..5de69a6310b9
--- /dev/null
+++ b/core/java/com/android/internal/util/ArtBinaryXmlPullParser.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * 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 com.android.internal.util;
+
+import android.annotation.NonNull;
+
+import java.io.DataInput;
+import java.io.InputStream;
+
+/**
+ * {@inheritDoc}
+ * <p>
+ * This decodes large code-points using 4-byte sequences, and <em>is not</em> compatible with the
+ * {@link DataInput} API contract, which specifies that large code-points must be encoded with
+ * 3-byte sequences.
+ */
+public class ArtBinaryXmlPullParser extends BinaryXmlPullParser {
+ @NonNull
+ protected FastDataInput obtainFastDataInput(@NonNull InputStream is) {
+ return ArtFastDataInput.obtain(is);
+ }
+}
diff --git a/core/java/com/android/internal/util/ArtBinaryXmlSerializer.java b/core/java/com/android/internal/util/ArtBinaryXmlSerializer.java
new file mode 100644
index 000000000000..cd534c96ca9d
--- /dev/null
+++ b/core/java/com/android/internal/util/ArtBinaryXmlSerializer.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * 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 com.android.internal.util;
+
+import android.annotation.NonNull;
+
+import java.io.DataOutput;
+import java.io.OutputStream;
+
+/**
+ * {@inheritDoc}
+ * <p>
+ * This encodes large code-points using 4-byte sequences and <em>is not</em> compatible with the
+ * {@link DataOutput} API contract, which specifies that large code-points must be encoded with
+ * 3-byte sequences.
+ */
+public class ArtBinaryXmlSerializer extends BinaryXmlSerializer {
+ @NonNull
+ @Override
+ protected FastDataOutput obtainFastDataOutput(@NonNull OutputStream os) {
+ return ArtFastDataOutput.obtain(os);
+ }
+}
diff --git a/core/java/com/android/internal/util/ArtFastDataInput.java b/core/java/com/android/internal/util/ArtFastDataInput.java
new file mode 100644
index 000000000000..25490215c146
--- /dev/null
+++ b/core/java/com/android/internal/util/ArtFastDataInput.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * 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 com.android.internal.util;
+
+import android.annotation.NonNull;
+import android.util.CharsetUtils;
+
+import java.io.DataInput;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * {@inheritDoc}
+ * <p>
+ * This decodes large code-points using 4-byte sequences, and <em>is not</em> compatible with the
+ * {@link DataInput} API contract, which specifies that large code-points must be encoded with
+ * 3-byte sequences.
+ */
+public class ArtFastDataInput extends FastDataInput {
+ private static AtomicReference<ArtFastDataInput> sInCache = new AtomicReference<>();
+
+ private final long mBufferPtr;
+
+ public ArtFastDataInput(@NonNull InputStream in, int bufferSize) {
+ super(in, bufferSize);
+
+ mBufferPtr = mRuntime.addressOf(mBuffer);
+ }
+
+ /**
+ * Obtain a {@link ArtFastDataInput} configured with the given
+ * {@link InputStream} and which decodes large code-points using 4-byte
+ * sequences.
+ * <p>
+ * This <em>is not</em> compatible with the {@link DataInput} API contract,
+ * which specifies that large code-points must be encoded with 3-byte
+ * sequences.
+ */
+ public static ArtFastDataInput obtain(@NonNull InputStream in) {
+ ArtFastDataInput instance = sInCache.getAndSet(null);
+ if (instance != null) {
+ instance.setInput(in);
+ return instance;
+ }
+ return new ArtFastDataInput(in, DEFAULT_BUFFER_SIZE);
+ }
+
+ /**
+ * Release a {@link ArtFastDataInput} to potentially be recycled. You must not
+ * interact with the object after releasing it.
+ */
+ public void release() {
+ super.release();
+
+ if (mBufferCap == DEFAULT_BUFFER_SIZE) {
+ // Try to return to the cache.
+ sInCache.compareAndSet(null, this);
+ }
+ }
+
+ @Override
+ public String readUTF() throws IOException {
+ // Attempt to read directly from buffer space if there's enough room,
+ // otherwise fall back to chunking into place
+ final int len = readUnsignedShort();
+ if (mBufferCap > len) {
+ if (mBufferLim - mBufferPos < len) fill(len);
+ final String res = CharsetUtils.fromModifiedUtf8Bytes(mBufferPtr, mBufferPos, len);
+ mBufferPos += len;
+ return res;
+ } else {
+ final byte[] tmp = (byte[]) mRuntime.newNonMovableArray(byte.class, len + 1);
+ readFully(tmp, 0, len);
+ return CharsetUtils.fromModifiedUtf8Bytes(mRuntime.addressOf(tmp), 0, len);
+ }
+ }
+}
diff --git a/core/java/com/android/internal/util/ArtFastDataOutput.java b/core/java/com/android/internal/util/ArtFastDataOutput.java
new file mode 100644
index 000000000000..de9e93bea7f1
--- /dev/null
+++ b/core/java/com/android/internal/util/ArtFastDataOutput.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * 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 com.android.internal.util;
+
+import android.annotation.NonNull;
+import android.util.CharsetUtils;
+
+import java.io.DataOutput;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * {@inheritDoc}
+ * <p>
+ * This encodes large code-points using 4-byte sequences and <em>is not</em> compatible with the
+ * {@link DataOutput} API contract, which specifies that large code-points must be encoded with
+ * 3-byte sequences.
+ */
+public class ArtFastDataOutput extends FastDataOutput {
+ private static AtomicReference<ArtFastDataOutput> sOutCache = new AtomicReference<>();
+
+ private final long mBufferPtr;
+
+ public ArtFastDataOutput(@NonNull OutputStream out, int bufferSize) {
+ super(out, bufferSize);
+
+ mBufferPtr = mRuntime.addressOf(mBuffer);
+ }
+
+ /**
+ * Obtain an {@link ArtFastDataOutput} configured with the given
+ * {@link OutputStream} and which encodes large code-points using 4-byte
+ * sequences.
+ * <p>
+ * This <em>is not</em> compatible with the {@link DataOutput} API contract,
+ * which specifies that large code-points must be encoded with 3-byte
+ * sequences.
+ */
+ public static ArtFastDataOutput obtain(@NonNull OutputStream out) {
+ ArtFastDataOutput instance = sOutCache.getAndSet(null);
+ if (instance != null) {
+ instance.setOutput(out);
+ return instance;
+ }
+ return new ArtFastDataOutput(out, DEFAULT_BUFFER_SIZE);
+ }
+
+ @Override
+ public void release() {
+ super.release();
+
+ if (mBufferCap == DEFAULT_BUFFER_SIZE) {
+ // Try to return to the cache.
+ sOutCache.compareAndSet(null, this);
+ }
+ }
+
+ @Override
+ public void writeUTF(String s) throws IOException {
+ // Attempt to write directly to buffer space if there's enough room,
+ // otherwise fall back to chunking into place
+ if (mBufferCap - mBufferPos < 2 + s.length()) drain();
+
+ // Magnitude of this returned value indicates the number of bytes
+ // required to encode the string; sign indicates success/failure
+ int len = CharsetUtils.toModifiedUtf8Bytes(s, mBufferPtr, mBufferPos + 2, mBufferCap);
+ if (Math.abs(len) > MAX_UNSIGNED_SHORT) {
+ throw new IOException("Modified UTF-8 length too large: " + len);
+ }
+
+ if (len >= 0) {
+ // Positive value indicates the string was encoded into the buffer
+ // successfully, so we only need to prefix with length
+ writeShort(len);
+ mBufferPos += len;
+ } else {
+ // Negative value indicates buffer was too small and we need to
+ // allocate a temporary buffer for encoding
+ len = -len;
+ final byte[] tmp = (byte[]) mRuntime.newNonMovableArray(byte.class, len + 1);
+ CharsetUtils.toModifiedUtf8Bytes(s, mRuntime.addressOf(tmp), 0, tmp.length);
+ writeShort(len);
+ write(tmp, 0, len);
+ }
+ }
+}
diff --git a/core/java/com/android/internal/util/BinaryXmlPullParser.java b/core/java/com/android/internal/util/BinaryXmlPullParser.java
index d3abac9f8877..f2e61be94d60 100644
--- a/core/java/com/android/internal/util/BinaryXmlPullParser.java
+++ b/core/java/com/android/internal/util/BinaryXmlPullParser.java
@@ -72,7 +72,7 @@ import java.util.Objects;
* <li>Namespaces, prefixes, properties, and options are unsupported.
* </ul>
*/
-public final class BinaryXmlPullParser implements TypedXmlPullParser {
+public class BinaryXmlPullParser implements TypedXmlPullParser {
private FastDataInput mIn;
private int mCurrentToken = START_DOCUMENT;
@@ -99,7 +99,7 @@ public final class BinaryXmlPullParser implements TypedXmlPullParser {
mIn = null;
}
- mIn = FastDataInput.obtainUsing4ByteSequences(is);
+ mIn = obtainFastDataInput(is);
mCurrentToken = START_DOCUMENT;
mCurrentDepth = 0;
@@ -129,6 +129,11 @@ public final class BinaryXmlPullParser implements TypedXmlPullParser {
}
}
+ @NonNull
+ protected FastDataInput obtainFastDataInput(@NonNull InputStream is) {
+ return FastDataInput.obtain(is);
+ }
+
@Override
public void setInput(Reader in) throws XmlPullParserException {
throw new UnsupportedOperationException();
diff --git a/core/java/com/android/internal/util/BinaryXmlSerializer.java b/core/java/com/android/internal/util/BinaryXmlSerializer.java
index 485430a43768..a9230e67c6fa 100644
--- a/core/java/com/android/internal/util/BinaryXmlSerializer.java
+++ b/core/java/com/android/internal/util/BinaryXmlSerializer.java
@@ -63,7 +63,7 @@ import java.util.Arrays;
* <li>Namespaces, prefixes, properties, and options are unsupported.
* </ul>
*/
-public final class BinaryXmlSerializer implements TypedXmlSerializer {
+public class BinaryXmlSerializer implements TypedXmlSerializer {
/**
* The wire protocol always begins with a well-known magic value of
* {@code ABX_}, representing "Android Binary XML." The final byte is a
@@ -118,13 +118,18 @@ public final class BinaryXmlSerializer implements TypedXmlSerializer {
throw new UnsupportedOperationException();
}
- mOut = FastDataOutput.obtainUsing4ByteSequences(os);
+ mOut = obtainFastDataOutput(os);
mOut.write(PROTOCOL_MAGIC_VERSION_0);
mTagCount = 0;
mTagNames = new String[8];
}
+ @NonNull
+ protected FastDataOutput obtainFastDataOutput(@NonNull OutputStream os) {
+ return FastDataOutput.obtain(os);
+ }
+
@Override
public void setOutput(Writer writer) {
throw new UnsupportedOperationException();
diff --git a/core/java/com/android/internal/util/FastDataInput.java b/core/java/com/android/internal/util/FastDataInput.java
index 5117034815fc..ed6697deb6af 100644
--- a/core/java/com/android/internal/util/FastDataInput.java
+++ b/core/java/com/android/internal/util/FastDataInput.java
@@ -17,7 +17,6 @@
package com.android.internal.util;
import android.annotation.NonNull;
-import android.util.CharsetUtils;
import dalvik.system.VMRuntime;
@@ -30,7 +29,6 @@ import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.Objects;
-import java.util.concurrent.atomic.AtomicReference;
/**
* Optimized implementation of {@link DataInput} which buffers data in memory
@@ -40,22 +38,18 @@ import java.util.concurrent.atomic.AtomicReference;
* {@link DataInputStream} with a {@link BufferedInputStream}.
*/
public class FastDataInput implements DataInput, Closeable {
- private static final int MAX_UNSIGNED_SHORT = 65_535;
+ protected static final int MAX_UNSIGNED_SHORT = 65_535;
- private static final int DEFAULT_BUFFER_SIZE = 32_768;
+ protected static final int DEFAULT_BUFFER_SIZE = 32_768;
- private static AtomicReference<FastDataInput> sInCache = new AtomicReference<>();
+ protected final VMRuntime mRuntime;
- private final VMRuntime mRuntime;
-
- private final byte[] mBuffer;
- private final long mBufferPtr;
- private final int mBufferCap;
- private final boolean mUse4ByteSequence;
+ protected final byte[] mBuffer;
+ protected final int mBufferCap;
private InputStream mIn;
- private int mBufferPos;
- private int mBufferLim;
+ protected int mBufferPos;
+ protected int mBufferLim;
/**
* Values that have been "interned" by {@link #readInternedUTF()}.
@@ -63,18 +57,7 @@ public class FastDataInput implements DataInput, Closeable {
private int mStringRefCount = 0;
private String[] mStringRefs = new String[32];
- /**
- * @deprecated callers must specify {@code use4ByteSequence} so they make a
- * clear choice about working around a long-standing ART bug, as
- * described by the {@code kUtfUse4ByteSequence} comments in
- * {@code art/runtime/jni/jni_internal.cc}.
- */
- @Deprecated
public FastDataInput(@NonNull InputStream in, int bufferSize) {
- this(in, bufferSize, true /* use4ByteSequence */);
- }
-
- public FastDataInput(@NonNull InputStream in, int bufferSize, boolean use4ByteSequence) {
mRuntime = VMRuntime.getRuntime();
mIn = Objects.requireNonNull(in);
if (bufferSize < 8) {
@@ -82,9 +65,7 @@ public class FastDataInput implements DataInput, Closeable {
}
mBuffer = (byte[]) mRuntime.newNonMovableArray(byte.class, bufferSize);
- mBufferPtr = mRuntime.addressOf(mBuffer);
mBufferCap = mBuffer.length;
- mUse4ByteSequence = use4ByteSequence;
}
/**
@@ -96,26 +77,8 @@ public class FastDataInput implements DataInput, Closeable {
* which specifies that large code-points must be encoded with 3-byte
* sequences.
*/
- public static FastDataInput obtainUsing3ByteSequences(@NonNull InputStream in) {
- return new FastDataInput(in, DEFAULT_BUFFER_SIZE, false /* use4ByteSequence */);
- }
-
- /**
- * Obtain a {@link FastDataInput} configured with the given
- * {@link InputStream} and which decodes large code-points using 4-byte
- * sequences.
- * <p>
- * This <em>is not</em> compatible with the {@link DataInput} API contract,
- * which specifies that large code-points must be encoded with 3-byte
- * sequences.
- */
- public static FastDataInput obtainUsing4ByteSequences(@NonNull InputStream in) {
- FastDataInput instance = sInCache.getAndSet(null);
- if (instance != null) {
- instance.setInput(in);
- return instance;
- }
- return new FastDataInput(in, DEFAULT_BUFFER_SIZE, true /* use4ByteSequence */);
+ public static FastDataInput obtain(@NonNull InputStream in) {
+ return new FastDataInput(in, DEFAULT_BUFFER_SIZE);
}
/**
@@ -127,24 +90,23 @@ public class FastDataInput implements DataInput, Closeable {
mBufferPos = 0;
mBufferLim = 0;
mStringRefCount = 0;
-
- if (mBufferCap == DEFAULT_BUFFER_SIZE && mUse4ByteSequence) {
- // Try to return to the cache.
- sInCache.compareAndSet(null, this);
- }
}
/**
* Re-initializes the object for the new input.
*/
- private void setInput(@NonNull InputStream in) {
+ protected void setInput(@NonNull InputStream in) {
+ if (mIn != null) {
+ throw new IllegalStateException("setInput() called before calling release()");
+ }
+
mIn = Objects.requireNonNull(in);
mBufferPos = 0;
mBufferLim = 0;
mStringRefCount = 0;
}
- private void fill(int need) throws IOException {
+ protected void fill(int need) throws IOException {
final int remain = mBufferLim - mBufferPos;
System.arraycopy(mBuffer, mBufferPos, mBuffer, 0, remain);
mBufferPos = 0;
@@ -202,30 +164,6 @@ public class FastDataInput implements DataInput, Closeable {
@Override
public String readUTF() throws IOException {
- if (mUse4ByteSequence) {
- return readUTFUsing4ByteSequences();
- } else {
- return readUTFUsing3ByteSequences();
- }
- }
-
- private String readUTFUsing4ByteSequences() throws IOException {
- // Attempt to read directly from buffer space if there's enough room,
- // otherwise fall back to chunking into place
- final int len = readUnsignedShort();
- if (mBufferCap > len) {
- if (mBufferLim - mBufferPos < len) fill(len);
- final String res = CharsetUtils.fromModifiedUtf8Bytes(mBufferPtr, mBufferPos, len);
- mBufferPos += len;
- return res;
- } else {
- final byte[] tmp = (byte[]) mRuntime.newNonMovableArray(byte.class, len + 1);
- readFully(tmp, 0, len);
- return CharsetUtils.fromModifiedUtf8Bytes(mRuntime.addressOf(tmp), 0, len);
- }
- }
-
- private String readUTFUsing3ByteSequences() throws IOException {
// Attempt to read directly from buffer space if there's enough room,
// otherwise fall back to chunking into place
final int len = readUnsignedShort();
diff --git a/core/java/com/android/internal/util/FastDataOutput.java b/core/java/com/android/internal/util/FastDataOutput.java
index 5b6075ec54ce..e394c7449801 100644
--- a/core/java/com/android/internal/util/FastDataOutput.java
+++ b/core/java/com/android/internal/util/FastDataOutput.java
@@ -17,7 +17,6 @@
package com.android.internal.util;
import android.annotation.NonNull;
-import android.util.CharsetUtils;
import dalvik.system.VMRuntime;
@@ -30,7 +29,6 @@ import java.io.IOException;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.Objects;
-import java.util.concurrent.atomic.AtomicReference;
/**
* Optimized implementation of {@link DataOutput} which buffers data in memory
@@ -40,48 +38,31 @@ import java.util.concurrent.atomic.AtomicReference;
* {@link DataOutputStream} with a {@link BufferedOutputStream}.
*/
public class FastDataOutput implements DataOutput, Flushable, Closeable {
- private static final int MAX_UNSIGNED_SHORT = 65_535;
+ protected static final int MAX_UNSIGNED_SHORT = 65_535;
- private static final int DEFAULT_BUFFER_SIZE = 32_768;
+ protected static final int DEFAULT_BUFFER_SIZE = 32_768;
- private static AtomicReference<FastDataOutput> sOutCache = new AtomicReference<>();
+ protected final VMRuntime mRuntime;
- private final VMRuntime mRuntime;
-
- private final byte[] mBuffer;
- private final long mBufferPtr;
- private final int mBufferCap;
- private final boolean mUse4ByteSequence;
+ protected final byte[] mBuffer;
+ protected final int mBufferCap;
private OutputStream mOut;
- private int mBufferPos;
+ protected int mBufferPos;
/**
* Values that have been "interned" by {@link #writeInternedUTF(String)}.
*/
private final HashMap<String, Integer> mStringRefs = new HashMap<>();
- /**
- * @deprecated callers must specify {@code use4ByteSequence} so they make a
- * clear choice about working around a long-standing ART bug, as
- * described by the {@code kUtfUse4ByteSequence} comments in
- * {@code art/runtime/jni/jni_internal.cc}.
- */
- @Deprecated
public FastDataOutput(@NonNull OutputStream out, int bufferSize) {
- this(out, bufferSize, true /* use4ByteSequence */);
- }
-
- public FastDataOutput(@NonNull OutputStream out, int bufferSize, boolean use4ByteSequence) {
mRuntime = VMRuntime.getRuntime();
if (bufferSize < 8) {
throw new IllegalArgumentException();
}
mBuffer = (byte[]) mRuntime.newNonMovableArray(byte.class, bufferSize);
- mBufferPtr = mRuntime.addressOf(mBuffer);
mBufferCap = mBuffer.length;
- mUse4ByteSequence = use4ByteSequence;
setOutput(out);
}
@@ -95,26 +76,8 @@ public class FastDataOutput implements DataOutput, Flushable, Closeable {
* which specifies that large code-points must be encoded with 3-byte
* sequences.
*/
- public static FastDataOutput obtainUsing3ByteSequences(@NonNull OutputStream out) {
- return new FastDataOutput(out, DEFAULT_BUFFER_SIZE, false /* use4ByteSequence */);
- }
-
- /**
- * Obtain a {@link FastDataOutput} configured with the given
- * {@link OutputStream} and which encodes large code-points using 4-byte
- * sequences.
- * <p>
- * This <em>is not</em> compatible with the {@link DataOutput} API contract,
- * which specifies that large code-points must be encoded with 3-byte
- * sequences.
- */
- public static FastDataOutput obtainUsing4ByteSequences(@NonNull OutputStream out) {
- FastDataOutput instance = sOutCache.getAndSet(null);
- if (instance != null) {
- instance.setOutput(out);
- return instance;
- }
- return new FastDataOutput(out, DEFAULT_BUFFER_SIZE, true /* use4ByteSequence */);
+ public static FastDataOutput obtain(@NonNull OutputStream out) {
+ return new FastDataOutput(out, DEFAULT_BUFFER_SIZE);
}
/**
@@ -129,23 +92,22 @@ public class FastDataOutput implements DataOutput, Flushable, Closeable {
mOut = null;
mBufferPos = 0;
mStringRefs.clear();
-
- if (mBufferCap == DEFAULT_BUFFER_SIZE && mUse4ByteSequence) {
- // Try to return to the cache.
- sOutCache.compareAndSet(null, this);
- }
}
/**
* Re-initializes the object for the new output.
*/
- private void setOutput(@NonNull OutputStream out) {
+ protected void setOutput(@NonNull OutputStream out) {
+ if (mOut != null) {
+ throw new IllegalStateException("setOutput() called before calling release()");
+ }
+
mOut = Objects.requireNonNull(out);
mBufferPos = 0;
mStringRefs.clear();
}
- private void drain() throws IOException {
+ protected void drain() throws IOException {
if (mBufferPos > 0) {
mOut.write(mBuffer, 0, mBufferPos);
mBufferPos = 0;
@@ -188,42 +150,6 @@ public class FastDataOutput implements DataOutput, Flushable, Closeable {
@Override
public void writeUTF(String s) throws IOException {
- if (mUse4ByteSequence) {
- writeUTFUsing4ByteSequences(s);
- } else {
- writeUTFUsing3ByteSequences(s);
- }
- }
-
- private void writeUTFUsing4ByteSequences(String s) throws IOException {
- // Attempt to write directly to buffer space if there's enough room,
- // otherwise fall back to chunking into place
- if (mBufferCap - mBufferPos < 2 + s.length()) drain();
-
- // Magnitude of this returned value indicates the number of bytes
- // required to encode the string; sign indicates success/failure
- int len = CharsetUtils.toModifiedUtf8Bytes(s, mBufferPtr, mBufferPos + 2, mBufferCap);
- if (Math.abs(len) > MAX_UNSIGNED_SHORT) {
- throw new IOException("Modified UTF-8 length too large: " + len);
- }
-
- if (len >= 0) {
- // Positive value indicates the string was encoded into the buffer
- // successfully, so we only need to prefix with length
- writeShort(len);
- mBufferPos += len;
- } else {
- // Negative value indicates buffer was too small and we need to
- // allocate a temporary buffer for encoding
- len = -len;
- final byte[] tmp = (byte[]) mRuntime.newNonMovableArray(byte.class, len + 1);
- CharsetUtils.toModifiedUtf8Bytes(s, mRuntime.addressOf(tmp), 0, tmp.length);
- writeShort(len);
- write(tmp, 0, len);
- }
- }
-
- private void writeUTFUsing3ByteSequences(String s) throws IOException {
final int len = (int) ModifiedUtf8.countBytes(s, false);
if (len > MAX_UNSIGNED_SHORT) {
throw new IOException("Modified UTF-8 length too large: " + len);
diff --git a/core/tests/coretests/src/com/android/internal/util/FastDataTest.java b/core/tests/coretests/src/com/android/internal/util/FastDataTest.java
index 04dfd6ee30e2..512ece38a229 100644
--- a/core/tests/coretests/src/com/android/internal/util/FastDataTest.java
+++ b/core/tests/coretests/src/com/android/internal/util/FastDataTest.java
@@ -39,6 +39,8 @@ import java.io.DataOutput;
import java.io.DataOutputStream;
import java.io.EOFException;
import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Collection;
@@ -61,14 +63,32 @@ public class FastDataTest {
this.use4ByteSequence = use4ByteSequence;
}
+ @NonNull
+ private FastDataInput createFastDataInput(@NonNull InputStream in, int bufferSize) {
+ if (use4ByteSequence) {
+ return new ArtFastDataInput(in, bufferSize);
+ } else {
+ return new FastDataInput(in, bufferSize);
+ }
+ }
+
+ @NonNull
+ private FastDataOutput createFastDataOutput(@NonNull OutputStream out, int bufferSize) {
+ if (use4ByteSequence) {
+ return new ArtFastDataOutput(out, bufferSize);
+ } else {
+ return new FastDataOutput(out, bufferSize);
+ }
+ }
+
@Test
public void testEndOfFile_Int() throws Exception {
- try (FastDataInput in = new FastDataInput(new ByteArrayInputStream(
- new byte[] { 1 }), 1000, use4ByteSequence)) {
+ try (FastDataInput in = createFastDataInput(new ByteArrayInputStream(
+ new byte[] { 1 }), 1000)) {
assertThrows(EOFException.class, () -> in.readInt());
}
- try (FastDataInput in = new FastDataInput(new ByteArrayInputStream(
- new byte[] { 1, 1, 1, 1 }), 1000, use4ByteSequence)) {
+ try (FastDataInput in = createFastDataInput(new ByteArrayInputStream(
+ new byte[] { 1, 1, 1, 1 }), 1000)) {
assertEquals(1, in.readByte());
assertThrows(EOFException.class, () -> in.readInt());
}
@@ -76,25 +96,25 @@ public class FastDataTest {
@Test
public void testEndOfFile_String() throws Exception {
- try (FastDataInput in = new FastDataInput(new ByteArrayInputStream(
- new byte[] { 1 }), 1000, use4ByteSequence)) {
+ try (FastDataInput in = createFastDataInput(new ByteArrayInputStream(
+ new byte[] { 1 }), 1000)) {
assertThrows(EOFException.class, () -> in.readUTF());
}
- try (FastDataInput in = new FastDataInput(new ByteArrayInputStream(
- new byte[] { 1, 1, 1, 1 }), 1000, use4ByteSequence)) {
+ try (FastDataInput in = createFastDataInput(new ByteArrayInputStream(
+ new byte[] { 1, 1, 1, 1 }), 1000)) {
assertThrows(EOFException.class, () -> in.readUTF());
}
}
@Test
public void testEndOfFile_Bytes_Small() throws Exception {
- try (FastDataInput in = new FastDataInput(new ByteArrayInputStream(
- new byte[] { 1, 1, 1, 1 }), 1000, use4ByteSequence)) {
+ try (FastDataInput in = createFastDataInput(new ByteArrayInputStream(
+ new byte[] { 1, 1, 1, 1 }), 1000)) {
final byte[] tmp = new byte[10];
assertThrows(EOFException.class, () -> in.readFully(tmp));
}
- try (FastDataInput in = new FastDataInput(new ByteArrayInputStream(
- new byte[] { 1, 1, 1, 1 }), 1000, use4ByteSequence)) {
+ try (FastDataInput in = createFastDataInput(new ByteArrayInputStream(
+ new byte[] { 1, 1, 1, 1 }), 1000)) {
final byte[] tmp = new byte[10_000];
assertThrows(EOFException.class, () -> in.readFully(tmp));
}
@@ -103,8 +123,7 @@ public class FastDataTest {
@Test
public void testUTF_Bounds() throws Exception {
final char[] buf = new char[65_534];
- try (FastDataOutput out = new FastDataOutput(new ByteArrayOutputStream(),
- BOUNCE_SIZE, use4ByteSequence)) {
+ try (FastDataOutput out = createFastDataOutput(new ByteArrayOutputStream(), BOUNCE_SIZE)) {
// Writing simple string will fit fine
Arrays.fill(buf, '!');
final String simple = new String(buf);
@@ -132,17 +151,15 @@ public class FastDataTest {
doTranscodeWrite(out);
out.flush();
- final FastDataInput in = new FastDataInput(
- new ByteArrayInputStream(outStream.toByteArray()),
- BOUNCE_SIZE, use4ByteSequence);
+ final FastDataInput in = createFastDataInput(
+ new ByteArrayInputStream(outStream.toByteArray()), BOUNCE_SIZE);
doTranscodeRead(in);
}
// Verify that fast data can be read by upstream
{
final ByteArrayOutputStream outStream = new ByteArrayOutputStream();
- final FastDataOutput out = new FastDataOutput(outStream,
- BOUNCE_SIZE, use4ByteSequence);
+ final FastDataOutput out = createFastDataOutput(outStream, BOUNCE_SIZE);
doTranscodeWrite(out);
out.flush();
@@ -299,7 +316,7 @@ public class FastDataTest {
final DataOutput slowData = new DataOutputStream(slowStream);
final ByteArrayOutputStream fastStream = new ByteArrayOutputStream();
- final FastDataOutput fastData = FastDataOutput.obtainUsing3ByteSequences(fastStream);
+ final FastDataOutput fastData = FastDataOutput.obtain(fastStream);
for (int cp = Character.MIN_CODE_POINT; cp < Character.MAX_CODE_POINT; cp++) {
if (Character.isValidCodePoint(cp)) {
@@ -416,16 +433,14 @@ public class FastDataTest {
private void doBounce(@NonNull ThrowingConsumer<FastDataOutput> out,
@NonNull ThrowingConsumer<FastDataInput> in, int count) throws Exception {
final ByteArrayOutputStream outStream = new ByteArrayOutputStream();
- final FastDataOutput outData = new FastDataOutput(outStream,
- BOUNCE_SIZE, use4ByteSequence);
+ final FastDataOutput outData = createFastDataOutput(outStream, BOUNCE_SIZE);
for (int i = 0; i < count; i++) {
out.accept(outData);
}
outData.flush();
final ByteArrayInputStream inStream = new ByteArrayInputStream(outStream.toByteArray());
- final FastDataInput inData = new FastDataInput(inStream,
- BOUNCE_SIZE, use4ByteSequence);
+ final FastDataInput inData = createFastDataInput(inStream, BOUNCE_SIZE);
for (int i = 0; i < count; i++) {
in.accept(inData);
}