mirror of
https://github.com/openjdk/jdk.git
synced 2026-01-28 12:09:14 +00:00
8366765: [REDO] Rename JavaLangAccess::*NoRepl methods
Reviewed-by: rriggs, liach, alanb
This commit is contained in:
parent
0d7f8f83c7
commit
a2f8d3c4c2
@ -604,14 +604,14 @@ public final class String
|
||||
}
|
||||
byte[] utf16 = StringUTF16.newBytesFor(length);
|
||||
StringLatin1.inflate(latin1, 0, utf16, 0, dp);
|
||||
dp = decodeUTF8_UTF16(latin1, sp, length, utf16, dp, true);
|
||||
dp = decodeUTF8_UTF16(latin1, sp, length, utf16, dp);
|
||||
if (dp != length) {
|
||||
utf16 = Arrays.copyOf(utf16, dp << 1);
|
||||
}
|
||||
return new String(utf16, UTF16);
|
||||
} else { // !COMPACT_STRINGS
|
||||
byte[] dst = StringUTF16.newBytesFor(length);
|
||||
int dp = decodeUTF8_UTF16(bytes, offset, offset + length, dst, 0, true);
|
||||
int dp = decodeUTF8_UTF16(bytes, offset, offset + length, dst, 0);
|
||||
if (dp != length) {
|
||||
dst = Arrays.copyOf(dst, dp << 1);
|
||||
}
|
||||
@ -688,13 +688,25 @@ public final class String
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Throws iae, instead of replacing, if malformed or unmappable.
|
||||
* The byte array can be exclusively used to construct
|
||||
* the string and is not modified or used for any other purpose.
|
||||
/**
|
||||
* {@return a new string by decoding from the given UTF-8 bytes array}
|
||||
* <p>
|
||||
* <b>WARNING: The caller of this method is assumed to have relinquished
|
||||
* and transferred the ownership of the byte array</b>. It can thus be
|
||||
* exclusively used to construct the {@code String}.
|
||||
*
|
||||
* @param bytes byte array containing UTF-8 encoded characters
|
||||
* @param offset the index of the first byte to decode
|
||||
* @param length the number of bytes to decode
|
||||
* @throws NullPointerException If {@code bytes} is null
|
||||
* @throws StringIndexOutOfBoundsException If {@code offset} is negative,
|
||||
* {@code length} is negative, or {@code offset} is greater than
|
||||
* {@code bytes.length - length}
|
||||
* @throws CharacterCodingException for malformed input or unmappable characters
|
||||
*/
|
||||
private static String newStringUTF8NoRepl(byte[] bytes, int offset, int length) {
|
||||
checkBoundsOffCount(offset, length, bytes.length);
|
||||
private static String newStringUTF8OrThrow(byte[] bytes, int offset, int length)
|
||||
throws CharacterCodingException {
|
||||
checkBoundsOffCount(offset, length, bytes.length); // Implicit null check on `bytes`
|
||||
if (length == 0) {
|
||||
return "";
|
||||
}
|
||||
@ -745,10 +757,10 @@ public final class String
|
||||
StringLatin1.inflate(dst, 0, buf, 0, dp);
|
||||
dst = buf;
|
||||
}
|
||||
dp = decodeUTF8_UTF16(bytes, offset, sl, dst, dp, false);
|
||||
dp = decodeUTF8_UTF16OrThrow(bytes, offset, sl, dst, dp);
|
||||
} else { // !COMPACT_STRINGS
|
||||
dst = StringUTF16.newBytesFor(length);
|
||||
dp = decodeUTF8_UTF16(bytes, offset, offset + length, dst, 0, false);
|
||||
dp = decodeUTF8_UTF16OrThrow(bytes, offset, offset + length, dst, 0);
|
||||
}
|
||||
if (dp != length) {
|
||||
dst = Arrays.copyOf(dst, dp << 1);
|
||||
@ -783,27 +795,16 @@ public final class String
|
||||
* @param cs charset the byte array encoded in
|
||||
*
|
||||
* @throws CharacterCodingException for malformed input or unmappable characters
|
||||
* @throws NullPointerException If {@code src} or {@code cs} is null
|
||||
*/
|
||||
static String newStringNoRepl(byte[] src, Charset cs) throws CharacterCodingException {
|
||||
try {
|
||||
return newStringNoRepl1(src, cs);
|
||||
} catch (IllegalArgumentException e) {
|
||||
//newStringNoRepl1 throws IAE with MalformedInputException or CCE as the cause
|
||||
Throwable cause = e.getCause();
|
||||
if (cause instanceof MalformedInputException mie) {
|
||||
throw mie;
|
||||
}
|
||||
throw (CharacterCodingException)cause;
|
||||
}
|
||||
}
|
||||
|
||||
private static String newStringNoRepl1(byte[] src, Charset cs) {
|
||||
int len = src.length;
|
||||
static String newStringOrThrow(byte[] src, Charset cs) throws CharacterCodingException {
|
||||
Objects.requireNonNull(cs);
|
||||
int len = src.length; // Implicit null check on `src`
|
||||
if (len == 0) {
|
||||
return "";
|
||||
}
|
||||
if (cs == UTF_8.INSTANCE) {
|
||||
return newStringUTF8NoRepl(src, 0, src.length);
|
||||
return newStringUTF8OrThrow(src, 0, src.length);
|
||||
}
|
||||
if (cs == ISO_8859_1.INSTANCE) {
|
||||
if (COMPACT_STRINGS)
|
||||
@ -816,7 +817,7 @@ public final class String
|
||||
return new String(src, LATIN1);
|
||||
return new String(StringLatin1.inflate(src, 0, src.length), UTF16);
|
||||
} else {
|
||||
throwMalformed(src);
|
||||
throw malformedASCII(src);
|
||||
}
|
||||
}
|
||||
|
||||
@ -831,13 +832,7 @@ public final class String
|
||||
}
|
||||
int en = scale(len, cd.maxCharsPerByte());
|
||||
char[] ca = new char[en];
|
||||
int caLen;
|
||||
try {
|
||||
caLen = decodeWithDecoder(cd, ca, src, 0, src.length);
|
||||
} catch (CharacterCodingException x) {
|
||||
// throw via IAE
|
||||
throw new IllegalArgumentException(x);
|
||||
}
|
||||
int caLen = decodeWithDecoder(cd, ca, src, 0, src.length);
|
||||
if (COMPACT_STRINGS) {
|
||||
byte[] val = StringUTF16.compress(ca, 0, caLen);
|
||||
byte coder = StringUTF16.coderFromArrayLen(val, caLen);
|
||||
@ -874,7 +869,7 @@ public final class String
|
||||
|
||||
private static byte[] encode(Charset cs, byte coder, byte[] val) {
|
||||
if (cs == UTF_8.INSTANCE) {
|
||||
return encodeUTF8(coder, val, true);
|
||||
return encodeUTF8(coder, val);
|
||||
}
|
||||
if (cs == ISO_8859_1.INSTANCE) {
|
||||
return encode8859_1(coder, val);
|
||||
@ -882,13 +877,30 @@ public final class String
|
||||
if (cs == US_ASCII.INSTANCE) {
|
||||
return encodeASCII(coder, val);
|
||||
}
|
||||
return encodeWithEncoder(cs, coder, val, true);
|
||||
return encodeWithEncoder(cs, coder, val, null);
|
||||
}
|
||||
|
||||
private static byte[] encodeWithEncoder(Charset cs, byte coder, byte[] val, boolean doReplace) {
|
||||
/**
|
||||
* {@return the byte array obtained by first decoding {@code val} with
|
||||
* {@code coder}, and then encoding the result with the encoder of {@code
|
||||
* cs}}
|
||||
*
|
||||
* @param cs a charset to obtain the encoder from
|
||||
* @param coder a coder to decode {@code val} with
|
||||
* @param val a string byte array encoded with {@code coder}
|
||||
* @param exClass The exception class where any non-null value indicates
|
||||
* malformed or unmappable bytes will result in an exception
|
||||
* to be thrown instead of getting replaced.
|
||||
* @param <E> The exception type parameter to enable callers to avoid
|
||||
* having to declare the exception
|
||||
*/
|
||||
private static <E extends Exception> byte[] encodeWithEncoder(
|
||||
Charset cs, byte coder, byte[] val, Class<E> exClass)
|
||||
throws E {
|
||||
CharsetEncoder ce = cs.newEncoder();
|
||||
int len = val.length >> coder; // assume LATIN1=0/UTF16=1;
|
||||
int en = scale(len, ce.maxBytesPerChar());
|
||||
boolean doReplace = exClass == null;
|
||||
// fastpath with ArrayEncoder implies `doReplace`.
|
||||
if (doReplace && ce instanceof ArrayEncoder ae) {
|
||||
// fastpath for ascii compatible
|
||||
@ -930,7 +942,9 @@ public final class String
|
||||
cr.throwException();
|
||||
} catch (CharacterCodingException x) {
|
||||
if (!doReplace) {
|
||||
throw new IllegalArgumentException(x);
|
||||
@SuppressWarnings("unchecked")
|
||||
E cce = (E) x;
|
||||
throw cce;
|
||||
} else {
|
||||
throw new Error(x);
|
||||
}
|
||||
@ -938,60 +952,69 @@ public final class String
|
||||
return trimArray(ba, bb.position());
|
||||
}
|
||||
|
||||
/*
|
||||
* Throws iae, instead of replacing, if unmappable.
|
||||
/**
|
||||
* {@return the sequence of bytes obtained by encoding the given string in UTF-8}
|
||||
*
|
||||
* @param s the string to encode
|
||||
* @throws NullPointerException If {@code s} is null
|
||||
* @throws CharacterCodingException For malformed input or unmappable characters
|
||||
*/
|
||||
static byte[] getBytesUTF8NoRepl(String s) {
|
||||
return encodeUTF8(s.coder(), s.value(), false);
|
||||
static byte[] getBytesUTF8OrThrow(String s) throws CharacterCodingException {
|
||||
return encodeUTF8OrThrow(s.coder(), s.value()); // Implicit null check on `s`
|
||||
}
|
||||
|
||||
private static boolean isASCII(byte[] src) {
|
||||
return !StringCoding.hasNegatives(src, 0, src.length);
|
||||
}
|
||||
|
||||
/*
|
||||
* Throws CCE, instead of replacing, if unmappable.
|
||||
/**
|
||||
* {@return the sequence of bytes obtained by encoding the given string in
|
||||
* the specified {@code Charset}}
|
||||
* <p>
|
||||
* <b>WARNING: This method returns the {@code byte[]} backing the provided
|
||||
* {@code String}, if the input is ASCII. Hence, the returned byte array
|
||||
* must not be modified.</b>
|
||||
*
|
||||
* @param s the string to encode
|
||||
* @param cs the charset
|
||||
* @throws NullPointerException If {@code s} or {@code cs} is null
|
||||
* @throws CharacterCodingException For malformed input or unmappable characters
|
||||
*/
|
||||
static byte[] getBytesNoRepl(String s, Charset cs) throws CharacterCodingException {
|
||||
try {
|
||||
return getBytesNoRepl1(s, cs);
|
||||
} catch (IllegalArgumentException e) {
|
||||
//getBytesNoRepl1 throws IAE with UnmappableCharacterException or CCE as the cause
|
||||
Throwable cause = e.getCause();
|
||||
if (cause instanceof UnmappableCharacterException) {
|
||||
throw (UnmappableCharacterException)cause;
|
||||
}
|
||||
throw (CharacterCodingException)cause;
|
||||
}
|
||||
}
|
||||
|
||||
private static byte[] getBytesNoRepl1(String s, Charset cs) {
|
||||
byte[] val = s.value();
|
||||
static byte[] getBytesOrThrow(String s, Charset cs) throws CharacterCodingException {
|
||||
Objects.requireNonNull(cs);
|
||||
byte[] val = s.value(); // Implicit null check on `s`
|
||||
byte coder = s.coder();
|
||||
if (cs == UTF_8.INSTANCE) {
|
||||
if (coder == LATIN1 && isASCII(val)) {
|
||||
return val;
|
||||
}
|
||||
return encodeUTF8(coder, val, false);
|
||||
return encodeUTF8OrThrow(coder, val);
|
||||
}
|
||||
if (cs == ISO_8859_1.INSTANCE) {
|
||||
if (coder == LATIN1) {
|
||||
return val;
|
||||
}
|
||||
return encode8859_1(coder, val, false);
|
||||
return encode8859_1OrThrow(coder, val);
|
||||
}
|
||||
if (cs == US_ASCII.INSTANCE) {
|
||||
if (coder == LATIN1) {
|
||||
if (isASCII(val)) {
|
||||
return val;
|
||||
} else {
|
||||
throwUnmappable(val);
|
||||
throw unmappableASCII(val);
|
||||
}
|
||||
}
|
||||
}
|
||||
return encodeWithEncoder(cs, coder, val, false);
|
||||
return encodeWithEncoder(cs, coder, val, CharacterCodingException.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@return the byte array obtained by first decoding {@code val} with
|
||||
* {@code coder}, and then encoding the result with US-ASCII}
|
||||
*
|
||||
* @param coder a coder to decode {@code val} with
|
||||
* @param val a string byte array encoded with {@code coder}
|
||||
*/
|
||||
private static byte[] encodeASCII(byte coder, byte[] val) {
|
||||
if (coder == LATIN1) {
|
||||
int positives = StringCoding.countPositives(val, 0, val.length);
|
||||
@ -1031,10 +1054,26 @@ public final class String
|
||||
}
|
||||
|
||||
private static byte[] encode8859_1(byte coder, byte[] val) {
|
||||
return encode8859_1(coder, val, true);
|
||||
return encode8859_1(coder, val, null);
|
||||
}
|
||||
|
||||
private static byte[] encode8859_1(byte coder, byte[] val, boolean doReplace) {
|
||||
private static byte[] encode8859_1OrThrow(byte coder, byte[] val) throws UnmappableCharacterException {
|
||||
return encode8859_1(coder, val, UnmappableCharacterException.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@return the byte array obtained by first decoding {@code val} with
|
||||
* {@code coder}, and then encoding the result with ISO-8859-1}
|
||||
*
|
||||
* @param coder a coder to decode {@code val} with
|
||||
* @param val a string byte array encoded with {@code coder}
|
||||
* @param exClass The exception class where any non-null value indicates
|
||||
* malformed or unmappable bytes will result in an exception
|
||||
* to be thrown instead of getting replaced.
|
||||
* @param <E> The exception type parameter to enable callers to avoid
|
||||
* having to declare the exception
|
||||
*/
|
||||
private static <E extends Exception> byte[] encode8859_1(byte coder, byte[] val, Class<E> exClass) throws E {
|
||||
if (coder == LATIN1) {
|
||||
return val.clone();
|
||||
}
|
||||
@ -1048,8 +1087,8 @@ public final class String
|
||||
sp = sp + ret;
|
||||
dp = dp + ret;
|
||||
if (ret != len) {
|
||||
if (!doReplace) {
|
||||
throwUnmappable(sp);
|
||||
if (exClass != null) {
|
||||
throw String.<E>unmappableCharacterException(sp);
|
||||
}
|
||||
char c = StringUTF16.getChar(val, sp++);
|
||||
if (Character.isHighSurrogate(c) && sp < sl &&
|
||||
@ -1143,7 +1182,26 @@ public final class String
|
||||
((byte) 0x80 << 0))));
|
||||
}
|
||||
|
||||
private static int decodeUTF8_UTF16(byte[] src, int sp, int sl, byte[] dst, int dp, boolean doReplace) {
|
||||
private static int decodeUTF8_UTF16(byte[] src, int sp, int sl, byte[] dst, int dp) {
|
||||
return decodeUTF8_UTF16(src, sp, sl, dst, dp, null);
|
||||
}
|
||||
|
||||
private static int decodeUTF8_UTF16OrThrow(
|
||||
byte[] src, int sp, int sl, byte[] dst, int dp)
|
||||
throws MalformedInputException {
|
||||
return decodeUTF8_UTF16(src, sp, sl, dst, dp, MalformedInputException.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param exClass The exception class where any non-null value indicates
|
||||
* malformed or unmappable bytes will result in an exception
|
||||
* to be thrown instead of getting replaced.
|
||||
* @param <E> The exception type parameter to enable callers to avoid
|
||||
* having to declare the exception
|
||||
*/
|
||||
private static <E extends Exception> int decodeUTF8_UTF16(
|
||||
byte[] src, int sp, int sl, byte[] dst, int dp, Class <E> exClass)
|
||||
throws E {
|
||||
while (sp < sl) {
|
||||
int b1 = src[sp++];
|
||||
if (b1 >= 0) {
|
||||
@ -1152,8 +1210,8 @@ public final class String
|
||||
if (sp < sl) {
|
||||
int b2 = src[sp++];
|
||||
if (isNotContinuation(b2)) {
|
||||
if (!doReplace) {
|
||||
throwMalformed(sp - 1, 1);
|
||||
if (exClass != null) {
|
||||
throw String.<E>malformedInputException(sp - 1, 1);
|
||||
}
|
||||
StringUTF16.putChar(dst, dp++, REPL);
|
||||
sp--;
|
||||
@ -1162,8 +1220,8 @@ public final class String
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (!doReplace) {
|
||||
throwMalformed(sp, 1); // underflow()
|
||||
if (exClass != null) {
|
||||
throw String.<E>malformedInputException(sp, 1); // underflow()
|
||||
}
|
||||
StringUTF16.putChar(dst, dp++, REPL);
|
||||
break;
|
||||
@ -1172,8 +1230,8 @@ public final class String
|
||||
int b2 = src[sp++];
|
||||
int b3 = src[sp++];
|
||||
if (isMalformed3(b1, b2, b3)) {
|
||||
if (!doReplace) {
|
||||
throwMalformed(sp - 3, 3);
|
||||
if (exClass != null) {
|
||||
throw String.<E>malformedInputException(sp - 3, 3);
|
||||
}
|
||||
StringUTF16.putChar(dst, dp++, REPL);
|
||||
sp -= 3;
|
||||
@ -1181,8 +1239,8 @@ public final class String
|
||||
} else {
|
||||
char c = decode3(b1, b2, b3);
|
||||
if (Character.isSurrogate(c)) {
|
||||
if (!doReplace) {
|
||||
throwMalformed(sp - 3, 3);
|
||||
if (exClass != null) {
|
||||
throw String.<E>malformedInputException(sp - 3, 3);
|
||||
}
|
||||
StringUTF16.putChar(dst, dp++, REPL);
|
||||
} else {
|
||||
@ -1192,14 +1250,14 @@ public final class String
|
||||
continue;
|
||||
}
|
||||
if (sp < sl && isMalformed3_2(b1, src[sp])) {
|
||||
if (!doReplace) {
|
||||
throwMalformed(sp - 1, 2);
|
||||
if (exClass != null) {
|
||||
throw String.<E>malformedInputException(sp - 1, 2);
|
||||
}
|
||||
StringUTF16.putChar(dst, dp++, REPL);
|
||||
continue;
|
||||
}
|
||||
if (!doReplace) {
|
||||
throwMalformed(sp, 1);
|
||||
if (exClass != null) {
|
||||
throw String.<E>malformedInputException(sp, 1);
|
||||
}
|
||||
StringUTF16.putChar(dst, dp++, REPL);
|
||||
break;
|
||||
@ -1211,8 +1269,8 @@ public final class String
|
||||
int uc = decode4(b1, b2, b3, b4);
|
||||
if (isMalformed4(b2, b3, b4) ||
|
||||
!Character.isSupplementaryCodePoint(uc)) { // shortest form check
|
||||
if (!doReplace) {
|
||||
throwMalformed(sp - 4, 4);
|
||||
if (exClass != null) {
|
||||
throw String.<E>malformedInputException(sp - 4, 4);
|
||||
}
|
||||
StringUTF16.putChar(dst, dp++, REPL);
|
||||
sp -= 4;
|
||||
@ -1225,14 +1283,14 @@ public final class String
|
||||
}
|
||||
b1 &= 0xff;
|
||||
if (b1 > 0xf4 || sp < sl && isMalformed4_2(b1, src[sp] & 0xff)) {
|
||||
if (!doReplace) {
|
||||
throwMalformed(sp - 1, 1); // or 2
|
||||
if (exClass != null) {
|
||||
throw String.<E>malformedInputException(sp - 1, 1); // or 2
|
||||
}
|
||||
StringUTF16.putChar(dst, dp++, REPL);
|
||||
continue;
|
||||
}
|
||||
if (!doReplace) {
|
||||
throwMalformed(sp - 1, 1);
|
||||
if (exClass != null) {
|
||||
throw String.<E>malformedInputException(sp - 1, 1);
|
||||
}
|
||||
sp++;
|
||||
StringUTF16.putChar(dst, dp++, REPL);
|
||||
@ -1241,8 +1299,8 @@ public final class String
|
||||
}
|
||||
break;
|
||||
} else {
|
||||
if (!doReplace) {
|
||||
throwMalformed(sp - 1, 1);
|
||||
if (exClass != null) {
|
||||
throw String.<E>malformedInputException(sp - 1, 1);
|
||||
}
|
||||
StringUTF16.putChar(dst, dp++, REPL);
|
||||
}
|
||||
@ -1284,29 +1342,76 @@ public final class String
|
||||
return 3;
|
||||
}
|
||||
|
||||
private static void throwMalformed(int off, int nb) {
|
||||
String msg = "malformed input off : " + off + ", length : " + nb;
|
||||
throw new IllegalArgumentException(msg, new MalformedInputException(nb));
|
||||
/**
|
||||
* {@return a new {@link MalformedInputException} for the sub-range denoted
|
||||
* by specified {@code offset} and {@code length}}
|
||||
*
|
||||
* @param <E> The exception type parameter to enable callers to avoid
|
||||
* having to declare the exception
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private static <E extends Exception> E malformedInputException(int offset, int length) {
|
||||
MalformedInputException mie = new MalformedInputException(length);
|
||||
String msg = "malformed input offset : " + offset + ", length : " + length;
|
||||
mie.initCause(new IllegalArgumentException(msg));
|
||||
return (E) mie;
|
||||
}
|
||||
|
||||
private static void throwMalformed(byte[] val) {
|
||||
/**
|
||||
* {@return a new {@link MalformedInputException} for the given malformed
|
||||
* ASCII string}
|
||||
*/
|
||||
private static MalformedInputException malformedASCII(byte[] val) {
|
||||
int dp = StringCoding.countPositives(val, 0, val.length);
|
||||
throwMalformed(dp, 1);
|
||||
return malformedInputException(dp, 1);
|
||||
}
|
||||
|
||||
private static void throwUnmappable(int off) {
|
||||
String msg = "malformed input off : " + off + ", length : 1";
|
||||
throw new IllegalArgumentException(msg, new UnmappableCharacterException(1));
|
||||
/**
|
||||
* {@return a new {@link UnmappableCharacterException} at given {@code offset}}
|
||||
*
|
||||
* @param <E> The exception type parameter to enable callers to avoid
|
||||
* having to declare the exception
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private static <E extends Exception> E unmappableCharacterException(int offset) {
|
||||
UnmappableCharacterException uce = new UnmappableCharacterException(1);
|
||||
String msg = "malformed input offset : " + offset + ", length : 1";
|
||||
uce.initCause(new IllegalArgumentException(msg, uce));
|
||||
return (E) uce;
|
||||
}
|
||||
|
||||
private static void throwUnmappable(byte[] val) {
|
||||
/**
|
||||
* {@return a new {@link UnmappableCharacterException} for the given
|
||||
* malformed ASCII string}
|
||||
*/
|
||||
private static UnmappableCharacterException unmappableASCII(byte[] val) {
|
||||
int dp = StringCoding.countPositives(val, 0, val.length);
|
||||
throwUnmappable(dp);
|
||||
return unmappableCharacterException(dp);
|
||||
}
|
||||
|
||||
private static byte[] encodeUTF8(byte coder, byte[] val, boolean doReplace) {
|
||||
private static byte[] encodeUTF8(byte coder, byte[] val) {
|
||||
return encodeUTF8(coder, val, null);
|
||||
}
|
||||
|
||||
private static byte[] encodeUTF8OrThrow(byte coder, byte[] val) throws UnmappableCharacterException {
|
||||
return encodeUTF8(coder, val, UnmappableCharacterException.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@return the byte array obtained by first decoding {@code val} with
|
||||
* {@code coder}, and then encoding the result with UTF-8}
|
||||
*
|
||||
* @param coder a coder to decode {@code val} with
|
||||
* @param val a string byte array encoded with {@code coder}
|
||||
* @param exClass The exception class where any non-null value indicates
|
||||
* malformed or unmappable bytes will result in an exception
|
||||
* to be thrown instead of getting replaced.
|
||||
* @param <E> The exception type parameter to enable callers to avoid
|
||||
* having to declare the exception
|
||||
*/
|
||||
private static <E extends Exception> byte[] encodeUTF8(byte coder, byte[] val, Class<E> exClass) throws E {
|
||||
if (coder == UTF16) {
|
||||
return encodeUTF8_UTF16(val, doReplace);
|
||||
return encodeUTF8_UTF16(val, exClass);
|
||||
}
|
||||
|
||||
int positives = StringCoding.countPositives(val, 0, val.length);
|
||||
@ -1334,13 +1439,24 @@ public final class String
|
||||
return Arrays.copyOf(dst, dp);
|
||||
}
|
||||
|
||||
private static byte[] encodeUTF8_UTF16(byte[] val, boolean doReplace) {
|
||||
/**
|
||||
* {@return the byte array obtained by first decoding {@code val} with
|
||||
* UTF-16, and then encoding the result with UTF-8}
|
||||
*
|
||||
* @param val a string byte array encoded with UTF-16
|
||||
* @param exClass The exception class where any non-null value indicates
|
||||
* malformed or unmappable bytes will result in an exception
|
||||
* to be thrown instead of getting replaced.
|
||||
* @param <E> The exception type parameter to enable callers to avoid
|
||||
* having to declare the exception
|
||||
*/
|
||||
private static <E extends Exception> byte[] encodeUTF8_UTF16(byte[] val, Class<E> exClass) throws E {
|
||||
int dp = 0;
|
||||
int sp = 0;
|
||||
int sl = val.length >> 1;
|
||||
// UTF-8 encoded can be as much as 3 times the string length
|
||||
// For very large estimate, (as in overflow of 32 bit int), precompute the exact size
|
||||
long allocLen = (sl * 3 < 0) ? computeSizeUTF8_UTF16(val, doReplace) : sl * 3;
|
||||
long allocLen = (sl * 3 < 0) ? computeSizeUTF8_UTF16(val, exClass) : sl * 3;
|
||||
if (allocLen > (long)Integer.MAX_VALUE) {
|
||||
throw new OutOfMemoryError("Required length exceeds implementation limit");
|
||||
}
|
||||
@ -1369,10 +1485,10 @@ public final class String
|
||||
uc = Character.toCodePoint(c, c2);
|
||||
}
|
||||
if (uc < 0) {
|
||||
if (doReplace) {
|
||||
if (exClass == null) {
|
||||
dst[dp++] = '?';
|
||||
} else {
|
||||
throwUnmappable(sp - 1);
|
||||
throw String.<E>unmappableCharacterException(sp - 1);
|
||||
}
|
||||
} else {
|
||||
dst[dp++] = (byte)(0xf0 | ((uc >> 18)));
|
||||
@ -1396,10 +1512,14 @@ public final class String
|
||||
|
||||
/**
|
||||
* {@return the exact size required to UTF_8 encode this UTF16 string}
|
||||
* @param val UTF16 encoded byte array
|
||||
* @param doReplace true to replace unmappable characters
|
||||
*
|
||||
* @param exClass The exception class where any non-null value indicates
|
||||
* malformed or unmappable bytes will result in an exception
|
||||
* to be thrown instead of getting discarded.
|
||||
* @param <E> The exception type parameter to enable callers to avoid
|
||||
* having to declare the exception
|
||||
*/
|
||||
private static long computeSizeUTF8_UTF16(byte[] val, boolean doReplace) {
|
||||
private static <E extends Exception> long computeSizeUTF8_UTF16(byte[] val, Class<E> exClass) throws E {
|
||||
long dp = 0L;
|
||||
int sp = 0;
|
||||
int sl = val.length >> 1;
|
||||
@ -1418,10 +1538,10 @@ public final class String
|
||||
uc = Character.toCodePoint(c, c2);
|
||||
}
|
||||
if (uc < 0) {
|
||||
if (doReplace) {
|
||||
if (exClass == null) {
|
||||
dp++;
|
||||
} else {
|
||||
throwUnmappable(sp - 1);
|
||||
throw String.<E>unmappableCharacterException(sp - 1);
|
||||
}
|
||||
} else {
|
||||
dp += 4;
|
||||
|
||||
@ -2124,6 +2124,7 @@ public final class System {
|
||||
public int countPositives(byte[] bytes, int offset, int length) {
|
||||
return StringCoding.countPositives(bytes, offset, length);
|
||||
}
|
||||
|
||||
public int countNonZeroAscii(String s) {
|
||||
return StringCoding.countNonZeroAscii(s);
|
||||
}
|
||||
@ -2132,21 +2133,24 @@ public final class System {
|
||||
return String.newStringWithLatin1Bytes(bytes);
|
||||
}
|
||||
|
||||
public String uncheckedNewStringNoRepl(byte[] bytes, Charset cs) throws CharacterCodingException {
|
||||
return String.newStringNoRepl(bytes, cs);
|
||||
public String uncheckedNewStringOrThrow(byte[] bytes, Charset cs) throws CharacterCodingException {
|
||||
return String.newStringOrThrow(bytes, cs);
|
||||
}
|
||||
|
||||
public char uncheckedGetUTF16Char(byte[] bytes, int index) {
|
||||
return StringUTF16.getChar(bytes, index);
|
||||
}
|
||||
|
||||
public void uncheckedPutCharUTF16(byte[] bytes, int index, int ch) {
|
||||
StringUTF16.putChar(bytes, index, ch);
|
||||
}
|
||||
public byte[] uncheckedGetBytesNoRepl(String s, Charset cs) throws CharacterCodingException {
|
||||
return String.getBytesNoRepl(s, cs);
|
||||
|
||||
public byte[] uncheckedGetBytesOrThrow(String s, Charset cs) throws CharacterCodingException {
|
||||
return String.getBytesOrThrow(s, cs);
|
||||
}
|
||||
|
||||
public byte[] getBytesUTF8NoRepl(String s) {
|
||||
return String.getBytesUTF8NoRepl(s);
|
||||
public byte[] getBytesUTF8OrThrow(String s) throws CharacterCodingException {
|
||||
return String.getBytesUTF8OrThrow(s);
|
||||
}
|
||||
|
||||
public void inflateBytesToChars(byte[] src, int srcOff, char[] dst, int dstOff, int len) {
|
||||
|
||||
@ -3043,7 +3043,7 @@ public final class Files {
|
||||
byte[] ba = readAllBytes(path);
|
||||
if (path.getClass().getModule() != Object.class.getModule())
|
||||
ba = ba.clone();
|
||||
return JLA.uncheckedNewStringNoRepl(ba, cs);
|
||||
return JLA.uncheckedNewStringOrThrow(ba, cs);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -3362,7 +3362,7 @@ public final class Files {
|
||||
Objects.requireNonNull(csq);
|
||||
Objects.requireNonNull(cs);
|
||||
|
||||
byte[] bytes = JLA.uncheckedGetBytesNoRepl(String.valueOf(csq), cs);
|
||||
byte[] bytes = JLA.uncheckedGetBytesOrThrow(String.valueOf(csq), cs);
|
||||
if (path.getClass().getModule() != Object.class.getModule())
|
||||
bytes = bytes.clone();
|
||||
write(path, bytes, options);
|
||||
|
||||
@ -256,7 +256,7 @@ class ZipCoder {
|
||||
try {
|
||||
// Copy subrange for exclusive use by the string being created
|
||||
byte[] bytes = Arrays.copyOfRange(ba, off, off + length);
|
||||
return JLA.uncheckedNewStringNoRepl(bytes, StandardCharsets.UTF_8);
|
||||
return JLA.uncheckedNewStringOrThrow(bytes, StandardCharsets.UTF_8);
|
||||
} catch (CharacterCodingException cce) {
|
||||
throw new IllegalArgumentException(cce);
|
||||
}
|
||||
@ -264,7 +264,11 @@ class ZipCoder {
|
||||
|
||||
@Override
|
||||
byte[] getBytes(String s) {
|
||||
return JLA.getBytesUTF8NoRepl(s);
|
||||
try {
|
||||
return JLA.getBytesUTF8OrThrow(s);
|
||||
} catch (CharacterCodingException cce) {
|
||||
throw new IllegalArgumentException(cce);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -278,8 +282,6 @@ class ZipCoder {
|
||||
// Non-ASCII, fall back to decoding a String
|
||||
// We avoid using decoder() here since the UTF8ZipCoder is
|
||||
// shared and that decoder is not thread safe.
|
||||
// We use the JLA.newStringUTF8NoRepl variant to throw
|
||||
// exceptions eagerly when opening ZipFiles
|
||||
return hash(toString(a, off, len));
|
||||
}
|
||||
int h = ArraysSupport.hashCodeOfUnsigned(a, off, len, 0);
|
||||
@ -296,7 +298,7 @@ class ZipCoder {
|
||||
@Override
|
||||
byte compare(String str, byte[] b, int off, int len, boolean matchDirectory) {
|
||||
try {
|
||||
byte[] encoded = JLA.uncheckedGetBytesNoRepl(str, UTF_8.INSTANCE);
|
||||
byte[] encoded = JLA.uncheckedGetBytesOrThrow(str, UTF_8.INSTANCE);
|
||||
int mismatch = Arrays.mismatch(encoded, 0, encoded.length, b, off, off+len);
|
||||
if (mismatch == -1) {
|
||||
return EXACT_MATCH;
|
||||
|
||||
@ -45,7 +45,6 @@ import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.RejectedExecutionException;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import jdk.internal.loader.NativeLibraries;
|
||||
@ -332,7 +331,7 @@ public interface JavaLangAccess {
|
||||
|
||||
/**
|
||||
* Constructs a new {@code String} by decoding the specified byte array
|
||||
* using the specified {@linkplain java.nio.charset.Charset charset}.
|
||||
* using the specified {@code Charset}.
|
||||
* <p>
|
||||
* <b>WARNING: The caller of this method shall relinquish and transfer the
|
||||
* ownership of the byte array to the callee</b>, since the latter will not
|
||||
@ -342,26 +341,24 @@ public interface JavaLangAccess {
|
||||
* @param cs the Charset
|
||||
* @return the newly created string
|
||||
* @throws CharacterCodingException for malformed or unmappable bytes
|
||||
* @throws NullPointerException If {@code bytes} or {@code cs} is null
|
||||
*/
|
||||
String uncheckedNewStringNoRepl(byte[] bytes, Charset cs) throws CharacterCodingException;
|
||||
String uncheckedNewStringOrThrow(byte[] bytes, Charset cs) throws CharacterCodingException;
|
||||
|
||||
/**
|
||||
* Encode the given string into a sequence of bytes using the specified
|
||||
* {@linkplain java.nio.charset.Charset charset}.
|
||||
* {@return the sequence of bytes obtained by encoding the given string in
|
||||
* the specified {@code Charset}}
|
||||
* <p>
|
||||
* <b>WARNING: This method returns the {@code byte[]} backing the provided
|
||||
* {@code String}, if the input is ASCII. Hence, the returned byte array
|
||||
* must not be modified.</b>
|
||||
* <p>
|
||||
* This method throws {@code CharacterCodingException} instead of replacing
|
||||
* when malformed input or unmappable characters are encountered.
|
||||
*
|
||||
* @param s the string to encode
|
||||
* @param cs the charset
|
||||
* @return the encoded bytes
|
||||
* @throws NullPointerException If {@code s} or {@code cs} is null
|
||||
* @throws CharacterCodingException for malformed input or unmappable characters
|
||||
*/
|
||||
byte[] uncheckedGetBytesNoRepl(String s, Charset cs) throws CharacterCodingException;
|
||||
byte[] uncheckedGetBytesOrThrow(String s, Charset cs) throws CharacterCodingException;
|
||||
|
||||
/**
|
||||
* Get the {@code char} at {@code index} in a {@code byte[]} in internal
|
||||
@ -387,13 +384,13 @@ public interface JavaLangAccess {
|
||||
void uncheckedPutCharUTF16(byte[] bytes, int index, int ch);
|
||||
|
||||
/**
|
||||
* Encode the given string into a sequence of bytes using utf8.
|
||||
* {@return the sequence of bytes obtained by encoding the given string in UTF-8}
|
||||
*
|
||||
* @param s the string to encode
|
||||
* @return the encoded bytes in utf8
|
||||
* @throws IllegalArgumentException for malformed surrogates
|
||||
* @throws NullPointerException If {@code s} is null
|
||||
* @throws CharacterCodingException For malformed input or unmappable characters
|
||||
*/
|
||||
byte[] getBytesUTF8NoRepl(String s);
|
||||
byte[] getBytesUTF8OrThrow(String s) throws CharacterCodingException;
|
||||
|
||||
/**
|
||||
* Inflated copy from {@code byte[]} to {@code char[]}, as defined by
|
||||
|
||||
@ -73,7 +73,7 @@ public final class StringSupport {
|
||||
final byte[] bytes = new byte[len];
|
||||
MemorySegment.copy(segment, JAVA_BYTE, offset, bytes, 0, len);
|
||||
try {
|
||||
return JAVA_LANG_ACCESS.uncheckedNewStringNoRepl(bytes, charset);
|
||||
return JAVA_LANG_ACCESS.uncheckedNewStringOrThrow(bytes, charset);
|
||||
} catch (CharacterCodingException _) {
|
||||
// use replacement characters for malformed input
|
||||
return new String(bytes, charset);
|
||||
@ -92,7 +92,7 @@ public final class StringSupport {
|
||||
byte[] bytes = new byte[len];
|
||||
MemorySegment.copy(segment, JAVA_BYTE, offset, bytes, 0, len);
|
||||
try {
|
||||
return JAVA_LANG_ACCESS.uncheckedNewStringNoRepl(bytes, charset);
|
||||
return JAVA_LANG_ACCESS.uncheckedNewStringOrThrow(bytes, charset);
|
||||
} catch (CharacterCodingException _) {
|
||||
// use replacement characters for malformed input
|
||||
return new String(bytes, charset);
|
||||
@ -111,7 +111,7 @@ public final class StringSupport {
|
||||
byte[] bytes = new byte[len];
|
||||
MemorySegment.copy(segment, JAVA_BYTE, offset, bytes, 0, len);
|
||||
try {
|
||||
return JAVA_LANG_ACCESS.uncheckedNewStringNoRepl(bytes, charset);
|
||||
return JAVA_LANG_ACCESS.uncheckedNewStringOrThrow(bytes, charset);
|
||||
} catch (CharacterCodingException _) {
|
||||
// use replacement characters for malformed input
|
||||
return new String(bytes, charset);
|
||||
|
||||
@ -126,7 +126,7 @@ class UnixPath implements Path {
|
||||
private static byte[] encode(UnixFileSystem fs, String input) {
|
||||
input = fs.normalizeNativePath(input);
|
||||
try {
|
||||
return JLA.uncheckedGetBytesNoRepl(input, Util.jnuEncoding());
|
||||
return JLA.uncheckedGetBytesOrThrow(input, Util.jnuEncoding());
|
||||
} catch (CharacterCodingException cce) {
|
||||
throw new InvalidPathException(input,
|
||||
"Malformed input or input contains unmappable characters");
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2022, 2025, Oracle and/or its affiliates. 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
|
||||
@ -24,8 +24,8 @@
|
||||
/*
|
||||
* @test
|
||||
* @bug 8286287 8288589
|
||||
* @summary Tests for *NoRepl() shared secret methods.
|
||||
* @run testng NoReplTest
|
||||
* @summary Tests for *OrThrow() shared secret methods.
|
||||
* @run testng OrThrowTest
|
||||
* @modules jdk.charsets
|
||||
*/
|
||||
|
||||
@ -39,17 +39,17 @@ import static java.nio.charset.StandardCharsets.UTF_16;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
@Test
|
||||
public class NoReplTest {
|
||||
public class OrThrowTest {
|
||||
private final static byte[] MALFORMED_UTF16 = {(byte)0x00, (byte)0x20, (byte)0x00};
|
||||
private final static String MALFORMED_WINDOWS_1252 = "\u0080\u041e";
|
||||
private final static Charset WINDOWS_1252 = Charset.forName("windows-1252");
|
||||
|
||||
/**
|
||||
* Verifies newStringNoRepl() throws a CharacterCodingException.
|
||||
* The method is invoked by `Files.readString()` method.
|
||||
* Verifies {@code uncheckedNewStringOrThrow()} throws a {@link CharacterCodingException}.
|
||||
* The method is invoked by {@code Files.readString()} method.
|
||||
*/
|
||||
@Test
|
||||
public void newStringNoReplTest() throws IOException {
|
||||
public void uncheckedNewStringOrThrowTest() throws IOException {
|
||||
var f = Files.createTempFile(null, null);
|
||||
try (var fos = Files.newOutputStream(f)) {
|
||||
fos.write(MALFORMED_UTF16);
|
||||
@ -67,11 +67,11 @@ public class NoReplTest {
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies getBytesNoRepl() throws a CharacterCodingException.
|
||||
* The method is invoked by `Files.writeString()` method.
|
||||
* Verifies {@code uncheckedGetBytesOrThrow()} throws a {@link CharacterCodingException}.
|
||||
* The method is invoked by {@code Files.writeString()} method.
|
||||
*/
|
||||
@Test
|
||||
public void getBytesNoReplTest() throws IOException {
|
||||
public void uncheckedGetBytesOrThrowTest() throws IOException {
|
||||
var f = Files.createTempFile(null, null);
|
||||
try {
|
||||
Files.writeString(f, MALFORMED_WINDOWS_1252, WINDOWS_1252);
|
||||
Loading…
x
Reference in New Issue
Block a user