8339699: Optimize DataOutputStream writeUTF
Reviewed-by: liach, bpb
This commit is contained in:
parent
559289487d
commit
b42fbf43df
@ -1,5 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 1994, 2023, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 1994, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||||
|
* Copyright (c) 2024, Alibaba Group Holding Limited. All Rights Reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -25,8 +26,13 @@
|
|||||||
|
|
||||||
package java.io;
|
package java.io;
|
||||||
|
|
||||||
|
import jdk.internal.access.JavaLangAccess;
|
||||||
|
import jdk.internal.access.SharedSecrets;
|
||||||
import jdk.internal.util.ByteArray;
|
import jdk.internal.util.ByteArray;
|
||||||
|
|
||||||
|
import static jdk.internal.util.ModifiedUtf.putChar;
|
||||||
|
import static jdk.internal.util.ModifiedUtf.utfLen;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A data output stream lets an application write primitive Java data
|
* A data output stream lets an application write primitive Java data
|
||||||
* types to an output stream in a portable way. An application can
|
* types to an output stream in a portable way. An application can
|
||||||
@ -44,6 +50,8 @@ import jdk.internal.util.ByteArray;
|
|||||||
* @since 1.0
|
* @since 1.0
|
||||||
*/
|
*/
|
||||||
public class DataOutputStream extends FilterOutputStream implements DataOutput {
|
public class DataOutputStream extends FilterOutputStream implements DataOutput {
|
||||||
|
private static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The number of bytes written to the data output stream so far.
|
* The number of bytes written to the data output stream so far.
|
||||||
* If this counter overflows, it will be wrapped to Integer.MAX_VALUE.
|
* If this counter overflows, it will be wrapped to Integer.MAX_VALUE.
|
||||||
@ -352,15 +360,11 @@ public class DataOutputStream extends FilterOutputStream implements DataOutput {
|
|||||||
* {@code str} would exceed 65535 bytes in length
|
* {@code str} would exceed 65535 bytes in length
|
||||||
* @throws IOException if some other I/O error occurs.
|
* @throws IOException if some other I/O error occurs.
|
||||||
*/
|
*/
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
static int writeUTF(String str, DataOutput out) throws IOException {
|
static int writeUTF(String str, DataOutput out) throws IOException {
|
||||||
final int strlen = str.length();
|
final int strlen = str.length();
|
||||||
int utflen = strlen; // optimized for ASCII
|
int countNonZeroAscii = JLA.countNonZeroAscii(str);
|
||||||
|
int utflen = utfLen(str, countNonZeroAscii);
|
||||||
for (int i = 0; i < strlen; i++) {
|
|
||||||
int c = str.charAt(i);
|
|
||||||
if (c >= 0x80 || c == 0)
|
|
||||||
utflen += (c >= 0x800) ? 2 : 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (utflen > 65535 || /* overflow */ utflen < strlen)
|
if (utflen > 65535 || /* overflow */ utflen < strlen)
|
||||||
throw new UTFDataFormatException(tooLongMsg(str, utflen));
|
throw new UTFDataFormatException(tooLongMsg(str, utflen));
|
||||||
@ -377,25 +381,11 @@ public class DataOutputStream extends FilterOutputStream implements DataOutput {
|
|||||||
int count = 0;
|
int count = 0;
|
||||||
ByteArray.setUnsignedShort(bytearr, count, utflen);
|
ByteArray.setUnsignedShort(bytearr, count, utflen);
|
||||||
count += 2;
|
count += 2;
|
||||||
int i = 0;
|
str.getBytes(0, countNonZeroAscii, bytearr, count);
|
||||||
for (i = 0; i < strlen; i++) { // optimized for initial run of ASCII
|
count += countNonZeroAscii;
|
||||||
int c = str.charAt(i);
|
|
||||||
if (c >= 0x80 || c == 0) break;
|
|
||||||
bytearr[count++] = (byte) c;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (; i < strlen; i++) {
|
for (int i = countNonZeroAscii; i < strlen;) {
|
||||||
int c = str.charAt(i);
|
count = putChar(bytearr, count, str.charAt(i++));
|
||||||
if (c < 0x80 && c != 0) {
|
|
||||||
bytearr[count++] = (byte) c;
|
|
||||||
} else if (c >= 0x800) {
|
|
||||||
bytearr[count++] = (byte) (0xE0 | ((c >> 12) & 0x0F));
|
|
||||||
bytearr[count++] = (byte) (0x80 | ((c >> 6) & 0x3F));
|
|
||||||
bytearr[count++] = (byte) (0x80 | ((c >> 0) & 0x3F));
|
|
||||||
} else {
|
|
||||||
bytearr[count++] = (byte) (0xC0 | ((c >> 6) & 0x1F));
|
|
||||||
bytearr[count++] = (byte) (0x80 | ((c >> 0) & 0x3F));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
out.write(bytearr, 0, utflen + 2);
|
out.write(bytearr, 0, utflen + 2);
|
||||||
return utflen + 2;
|
return utflen + 2;
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 1996, 2024, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 1996, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||||
|
* Copyright (c) 2024, Alibaba Group Holding Limited. All Rights Reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -34,8 +35,13 @@ import java.util.Objects;
|
|||||||
import java.util.StringJoiner;
|
import java.util.StringJoiner;
|
||||||
|
|
||||||
import jdk.internal.util.ByteArray;
|
import jdk.internal.util.ByteArray;
|
||||||
|
import jdk.internal.access.JavaLangAccess;
|
||||||
|
import jdk.internal.access.SharedSecrets;
|
||||||
import sun.reflect.misc.ReflectUtil;
|
import sun.reflect.misc.ReflectUtil;
|
||||||
|
|
||||||
|
import static jdk.internal.util.ModifiedUtf.putChar;
|
||||||
|
import static jdk.internal.util.ModifiedUtf.utfLen;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An ObjectOutputStream writes primitive data types and graphs of Java objects
|
* An ObjectOutputStream writes primitive data types and graphs of Java objects
|
||||||
* to an OutputStream. The objects can be read (reconstituted) using an
|
* to an OutputStream. The objects can be read (reconstituted) using an
|
||||||
@ -169,6 +175,7 @@ import sun.reflect.misc.ReflectUtil;
|
|||||||
public class ObjectOutputStream
|
public class ObjectOutputStream
|
||||||
extends OutputStream implements ObjectOutput, ObjectStreamConstants
|
extends OutputStream implements ObjectOutput, ObjectStreamConstants
|
||||||
{
|
{
|
||||||
|
private static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess();
|
||||||
|
|
||||||
private static class Caches {
|
private static class Caches {
|
||||||
/** cache of subclass security audit results */
|
/** cache of subclass security audit results */
|
||||||
@ -885,7 +892,7 @@ public class ObjectOutputStream
|
|||||||
* stream
|
* stream
|
||||||
*/
|
*/
|
||||||
public void writeUTF(String str) throws IOException {
|
public void writeUTF(String str) throws IOException {
|
||||||
bout.writeUTF(str);
|
bout.writeUTFInternal(str, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1317,14 +1324,7 @@ public class ObjectOutputStream
|
|||||||
*/
|
*/
|
||||||
private void writeString(String str, boolean unshared) throws IOException {
|
private void writeString(String str, boolean unshared) throws IOException {
|
||||||
handles.assign(unshared ? null : str);
|
handles.assign(unshared ? null : str);
|
||||||
long utflen = bout.getUTFLength(str);
|
bout.writeUTFInternal(str, true);
|
||||||
if (utflen <= 0xFFFF) {
|
|
||||||
bout.writeByte(TC_STRING);
|
|
||||||
bout.writeUTF(str, utflen);
|
|
||||||
} else {
|
|
||||||
bout.writeByte(TC_LONGSTRING);
|
|
||||||
bout.writeLongUTF(str, utflen);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1994,26 +1994,27 @@ public class ObjectOutputStream
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void writeBytes(String s) throws IOException {
|
@SuppressWarnings("deprecation")
|
||||||
int endoff = s.length();
|
void writeBytes(String s, int len) throws IOException {
|
||||||
int cpos = 0;
|
int pos = this.pos;
|
||||||
int csize = 0;
|
for (int strpos = 0; strpos < len;) {
|
||||||
for (int off = 0; off < endoff; ) {
|
int rem = MAX_BLOCK_SIZE - pos;
|
||||||
if (cpos >= csize) {
|
int csize = Math.min(len - strpos, rem);
|
||||||
cpos = 0;
|
s.getBytes(strpos, strpos + csize, buf, pos);
|
||||||
csize = Math.min(endoff - off, CHAR_BUF_SIZE);
|
pos += csize;
|
||||||
s.getChars(off, off + csize, cbuf, 0);
|
strpos += csize;
|
||||||
}
|
|
||||||
if (pos >= MAX_BLOCK_SIZE) {
|
if (pos == MAX_BLOCK_SIZE) {
|
||||||
|
this.pos = pos;
|
||||||
drain();
|
drain();
|
||||||
|
pos = 0;
|
||||||
}
|
}
|
||||||
int n = Math.min(csize - cpos, MAX_BLOCK_SIZE - pos);
|
|
||||||
int stop = pos + n;
|
|
||||||
while (pos < stop) {
|
|
||||||
buf[pos++] = (byte) cbuf[cpos++];
|
|
||||||
}
|
|
||||||
off += n;
|
|
||||||
}
|
}
|
||||||
|
this.pos = pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void writeBytes(String s) throws IOException {
|
||||||
|
writeBytes(s, s.length());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void writeChars(String s) throws IOException {
|
public void writeChars(String s) throws IOException {
|
||||||
@ -2026,8 +2027,47 @@ public class ObjectOutputStream
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void writeUTF(String s) throws IOException {
|
public void writeUTF(String str) throws IOException {
|
||||||
writeUTF(s, getUTFLength(s));
|
writeUTFInternal(str, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeUTFInternal(String str, boolean writeHeader) throws IOException {
|
||||||
|
int strlen = str.length();
|
||||||
|
int countNonZeroAscii = JLA.countNonZeroAscii(str);
|
||||||
|
int utflen = utfLen(str, countNonZeroAscii);
|
||||||
|
if (utflen <= 0xFFFF) {
|
||||||
|
if(writeHeader) {
|
||||||
|
writeByte(TC_STRING);
|
||||||
|
}
|
||||||
|
writeShort(utflen);
|
||||||
|
} else {
|
||||||
|
if(writeHeader) {
|
||||||
|
writeByte(TC_LONGSTRING);
|
||||||
|
}
|
||||||
|
writeLong(utflen);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (countNonZeroAscii != 0) {
|
||||||
|
writeBytes(str, countNonZeroAscii);
|
||||||
|
}
|
||||||
|
if (countNonZeroAscii != strlen) {
|
||||||
|
writeMoreUTF(str, countNonZeroAscii);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeMoreUTF(String str, int stroff) throws IOException {
|
||||||
|
int pos = this.pos;
|
||||||
|
for (int strlen = str.length(); stroff < strlen;) {
|
||||||
|
char c = str.charAt(stroff++);
|
||||||
|
int csize = c != 0 && c < 0x80 ? 1 : c >= 0x800 ? 3 : 2;
|
||||||
|
if (pos + csize >= MAX_BLOCK_SIZE) {
|
||||||
|
this.pos = pos;
|
||||||
|
drain();
|
||||||
|
pos = 0;
|
||||||
|
}
|
||||||
|
pos = putChar(buf, pos, c);
|
||||||
|
}
|
||||||
|
this.pos = pos;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -2153,112 +2193,6 @@ public class ObjectOutputStream
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the length in bytes of the UTF encoding of the given string.
|
|
||||||
*/
|
|
||||||
long getUTFLength(String s) {
|
|
||||||
int len = s.length();
|
|
||||||
long utflen = 0;
|
|
||||||
for (int off = 0; off < len; ) {
|
|
||||||
int csize = Math.min(len - off, CHAR_BUF_SIZE);
|
|
||||||
s.getChars(off, off + csize, cbuf, 0);
|
|
||||||
for (int cpos = 0; cpos < csize; cpos++) {
|
|
||||||
char c = cbuf[cpos];
|
|
||||||
if (c >= 0x0001 && c <= 0x007F) {
|
|
||||||
utflen++;
|
|
||||||
} else if (c > 0x07FF) {
|
|
||||||
utflen += 3;
|
|
||||||
} else {
|
|
||||||
utflen += 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
off += csize;
|
|
||||||
}
|
|
||||||
return utflen;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Writes the given string in UTF format. This method is used in
|
|
||||||
* situations where the UTF encoding length of the string is already
|
|
||||||
* known; specifying it explicitly avoids a prescan of the string to
|
|
||||||
* determine its UTF length.
|
|
||||||
*/
|
|
||||||
void writeUTF(String s, long utflen) throws IOException {
|
|
||||||
if (utflen > 0xFFFFL) {
|
|
||||||
throw new UTFDataFormatException();
|
|
||||||
}
|
|
||||||
writeShort((int) utflen);
|
|
||||||
if (utflen == (long) s.length()) {
|
|
||||||
writeBytes(s);
|
|
||||||
} else {
|
|
||||||
writeUTFBody(s);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Writes given string in "long" UTF format. "Long" UTF format is
|
|
||||||
* identical to standard UTF, except that it uses an 8 byte header
|
|
||||||
* (instead of the standard 2 bytes) to convey the UTF encoding length.
|
|
||||||
*/
|
|
||||||
void writeLongUTF(String s) throws IOException {
|
|
||||||
writeLongUTF(s, getUTFLength(s));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Writes given string in "long" UTF format, where the UTF encoding
|
|
||||||
* length of the string is already known.
|
|
||||||
*/
|
|
||||||
void writeLongUTF(String s, long utflen) throws IOException {
|
|
||||||
writeLong(utflen);
|
|
||||||
if (utflen == (long) s.length()) {
|
|
||||||
writeBytes(s);
|
|
||||||
} else {
|
|
||||||
writeUTFBody(s);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Writes the "body" (i.e., the UTF representation minus the 2-byte or
|
|
||||||
* 8-byte length header) of the UTF encoding for the given string.
|
|
||||||
*/
|
|
||||||
private void writeUTFBody(String s) throws IOException {
|
|
||||||
int limit = MAX_BLOCK_SIZE - 3;
|
|
||||||
int len = s.length();
|
|
||||||
for (int off = 0; off < len; ) {
|
|
||||||
int csize = Math.min(len - off, CHAR_BUF_SIZE);
|
|
||||||
s.getChars(off, off + csize, cbuf, 0);
|
|
||||||
for (int cpos = 0; cpos < csize; cpos++) {
|
|
||||||
char c = cbuf[cpos];
|
|
||||||
if (pos <= limit) {
|
|
||||||
if (c <= 0x007F && c != 0) {
|
|
||||||
buf[pos++] = (byte) c;
|
|
||||||
} else if (c > 0x07FF) {
|
|
||||||
buf[pos + 2] = (byte) (0x80 | ((c >> 0) & 0x3F));
|
|
||||||
buf[pos + 1] = (byte) (0x80 | ((c >> 6) & 0x3F));
|
|
||||||
buf[pos + 0] = (byte) (0xE0 | ((c >> 12) & 0x0F));
|
|
||||||
pos += 3;
|
|
||||||
} else {
|
|
||||||
buf[pos + 1] = (byte) (0x80 | ((c >> 0) & 0x3F));
|
|
||||||
buf[pos + 0] = (byte) (0xC0 | ((c >> 6) & 0x1F));
|
|
||||||
pos += 2;
|
|
||||||
}
|
|
||||||
} else { // write one byte at a time to normalize block
|
|
||||||
if (c <= 0x007F && c != 0) {
|
|
||||||
write(c);
|
|
||||||
} else if (c > 0x07FF) {
|
|
||||||
write(0xE0 | ((c >> 12) & 0x0F));
|
|
||||||
write(0x80 | ((c >> 6) & 0x3F));
|
|
||||||
write(0x80 | ((c >> 0) & 0x3F));
|
|
||||||
} else {
|
|
||||||
write(0xC0 | ((c >> 6) & 0x1F));
|
|
||||||
write(0x80 | ((c >> 0) & 0x3F));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
off += csize;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -38,6 +38,9 @@ import jdk.internal.access.JavaLangAccess;
|
|||||||
import jdk.internal.access.SharedSecrets;
|
import jdk.internal.access.SharedSecrets;
|
||||||
import jdk.internal.vm.annotation.ForceInline;
|
import jdk.internal.vm.annotation.ForceInline;
|
||||||
|
|
||||||
|
import static jdk.internal.util.ModifiedUtf.putChar;
|
||||||
|
import static jdk.internal.util.ModifiedUtf.utfLen;
|
||||||
|
|
||||||
public final class BufWriterImpl implements BufWriter {
|
public final class BufWriterImpl implements BufWriter {
|
||||||
private static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess();
|
private static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess();
|
||||||
|
|
||||||
@ -162,14 +165,7 @@ public final class BufWriterImpl implements BufWriter {
|
|||||||
void writeUTF(String str) {
|
void writeUTF(String str) {
|
||||||
int strlen = str.length();
|
int strlen = str.length();
|
||||||
int countNonZeroAscii = JLA.countNonZeroAscii(str);
|
int countNonZeroAscii = JLA.countNonZeroAscii(str);
|
||||||
int utflen = strlen;
|
int utflen = utfLen(str, countNonZeroAscii);
|
||||||
if (countNonZeroAscii != strlen) {
|
|
||||||
for (int i = countNonZeroAscii; i < strlen; i++) {
|
|
||||||
int c = str.charAt(i);
|
|
||||||
if (c >= 0x80 || c == 0)
|
|
||||||
utflen += (c >= 0x800) ? 2 : 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (utflen > 65535) {
|
if (utflen > 65535) {
|
||||||
throw new IllegalArgumentException("string too long");
|
throw new IllegalArgumentException("string too long");
|
||||||
}
|
}
|
||||||
@ -185,20 +181,8 @@ public final class BufWriterImpl implements BufWriter {
|
|||||||
str.getBytes(0, countNonZeroAscii, elems, offset);
|
str.getBytes(0, countNonZeroAscii, elems, offset);
|
||||||
offset += countNonZeroAscii;
|
offset += countNonZeroAscii;
|
||||||
|
|
||||||
for (int i = countNonZeroAscii; i < strlen; ++i) {
|
for (int i = countNonZeroAscii; i < strlen; i++) {
|
||||||
char c = str.charAt(i);
|
offset = putChar(elems, offset, str.charAt(i));
|
||||||
if (c >= '\001' && c <= '\177') {
|
|
||||||
elems[offset++] = (byte) c;
|
|
||||||
} else if (c > '\u07FF') {
|
|
||||||
elems[offset ] = (byte) (0xE0 | c >> 12 & 0xF);
|
|
||||||
elems[offset + 1] = (byte) (0x80 | c >> 6 & 0x3F);
|
|
||||||
elems[offset + 2] = (byte) (0x80 | c & 0x3F);
|
|
||||||
offset += 3;
|
|
||||||
} else {
|
|
||||||
elems[offset ] = (byte) (0xC0 | c >> 6 & 0x1F);
|
|
||||||
elems[offset + 1] = (byte) (0x80 | c & 0x3F);
|
|
||||||
offset += 2;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.offset = offset;
|
this.offset = offset;
|
||||||
|
@ -0,0 +1,71 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2024, Alibaba Group Holding Limited. All Rights Reserved.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License version 2 only, as
|
||||||
|
* published by the Free Software Foundation. Oracle designates this
|
||||||
|
* particular file as subject to the "Classpath" exception as provided
|
||||||
|
* by Oracle in the LICENSE file that accompanied this code.
|
||||||
|
*
|
||||||
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* version 2 for more details (a copy is included in the LICENSE file that
|
||||||
|
* accompanied this code).
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License version
|
||||||
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*
|
||||||
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||||
|
* or visit www.oracle.com if you need additional information or have any
|
||||||
|
* questions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package jdk.internal.util;
|
||||||
|
|
||||||
|
import jdk.internal.vm.annotation.ForceInline;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper to JDK UTF putChar and Calculate length
|
||||||
|
*
|
||||||
|
* @since 24
|
||||||
|
*/
|
||||||
|
public abstract class ModifiedUtf {
|
||||||
|
private ModifiedUtf() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@ForceInline
|
||||||
|
public static int putChar(byte[] buf, int offset, char c) {
|
||||||
|
if (c != 0 && c < 0x80) {
|
||||||
|
buf[offset++] = (byte) c;
|
||||||
|
} else if (c >= 0x800) {
|
||||||
|
buf[offset ] = (byte) (0xE0 | c >> 12 & 0x0F);
|
||||||
|
buf[offset + 1] = (byte) (0x80 | c >> 6 & 0x3F);
|
||||||
|
buf[offset + 2] = (byte) (0x80 | c & 0x3F);
|
||||||
|
offset += 3;
|
||||||
|
} else {
|
||||||
|
buf[offset ] = (byte) (0xC0 | c >> 6 & 0x1F);
|
||||||
|
buf[offset + 1] = (byte) (0x80 | c & 0x3F);
|
||||||
|
offset += 2;
|
||||||
|
}
|
||||||
|
return offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate the utf length of a string
|
||||||
|
* @param str input string
|
||||||
|
* @param countNonZeroAscii the number of non-zero ascii characters in the prefix calculated by JLA.countNonZeroAscii(str)
|
||||||
|
*/
|
||||||
|
@ForceInline
|
||||||
|
public static int utfLen(String str, int countNonZeroAscii) {
|
||||||
|
int utflen = str.length();
|
||||||
|
for (int i = utflen - 1; i >= countNonZeroAscii; i--) {
|
||||||
|
int c = str.charAt(i);
|
||||||
|
if (c >= 0x80 || c == 0)
|
||||||
|
utflen += (c >= 0x800) ? 2 : 1;
|
||||||
|
}
|
||||||
|
return utflen;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,91 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2024, Alibaba Group Holding Limited. All Rights Reserved.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License version 2 only, as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* version 2 for more details (a copy is included in the LICENSE file that
|
||||||
|
* accompanied this code).
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License version
|
||||||
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*
|
||||||
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||||
|
* or visit www.oracle.com if you need additional information or have any
|
||||||
|
* questions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.openjdk.bench.java.io;
|
||||||
|
|
||||||
|
import org.openjdk.jmh.annotations.*;
|
||||||
|
import org.openjdk.jmh.infra.Blackhole;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.HexFormat;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
@BenchmarkMode(Mode.AverageTime)
|
||||||
|
@OutputTimeUnit(TimeUnit.MICROSECONDS)
|
||||||
|
@Fork(2)
|
||||||
|
@Measurement(iterations = 6, time = 1)
|
||||||
|
@Warmup(iterations = 4, time = 2)
|
||||||
|
@State(Scope.Thread)
|
||||||
|
public class DataOutputStreamBench {
|
||||||
|
|
||||||
|
@Param({"ascii", "utf8_2_bytes", "utf8_3_bytes", "emoji"})
|
||||||
|
public String charType;
|
||||||
|
|
||||||
|
ByteArrayOutputStream bytesOutput;
|
||||||
|
DataOutputStream dataOutput;
|
||||||
|
ObjectOutputStream objectOutput;
|
||||||
|
String[] strings;
|
||||||
|
|
||||||
|
@Setup(Level.Trial)
|
||||||
|
public void setup() throws Exception {
|
||||||
|
byte[] bytes = HexFormat.of().parseHex(
|
||||||
|
switch (charType) {
|
||||||
|
case "ascii" -> "78";
|
||||||
|
case "utf8_2_bytes" -> "c2a9";
|
||||||
|
case "utf8_3_bytes" -> "e6b8a9";
|
||||||
|
case "emoji" -> "e29da3efb88f";
|
||||||
|
default -> throw new IllegalArgumentException("bad charType: " + charType);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
String s = new String(bytes, 0, bytes.length, StandardCharsets.UTF_8);
|
||||||
|
strings = new String[128];
|
||||||
|
for (int i = 0; i < strings.length; i++) {
|
||||||
|
strings[i] = "A".repeat(i).concat(s.repeat(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
bytesOutput = new ByteArrayOutputStream(1024 * 64);
|
||||||
|
dataOutput = new DataOutputStream(bytesOutput);
|
||||||
|
objectOutput = new ObjectOutputStream(bytesOutput);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Benchmark
|
||||||
|
public void dataOutwriteUTF(Blackhole bh) throws Exception {
|
||||||
|
bytesOutput.reset();
|
||||||
|
for (var s : strings) {
|
||||||
|
dataOutput.writeUTF(s);
|
||||||
|
}
|
||||||
|
dataOutput.flush();
|
||||||
|
bh.consume(bytesOutput.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Benchmark
|
||||||
|
public void objectWriteUTF(Blackhole bh) throws Exception {
|
||||||
|
bytesOutput.reset();
|
||||||
|
for (var s : strings) {
|
||||||
|
objectOutput.writeUTF(s);
|
||||||
|
}
|
||||||
|
objectOutput.flush();
|
||||||
|
bh.consume(bytesOutput.size());
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user