mirror of
https://github.com/spring-projects/spring-boot.git
synced 2026-09-17 12:09:16 +00:00
Merge pull request #51464 from yyuneu
Closes gh-51464 * gh-51464: Fix corruption of supplementary characters in AppendableByteArray
This commit is contained in:
+22
-3
@@ -56,6 +56,8 @@ class AppendableByteArray implements Appendable {
|
||||
|
||||
private ByteBuffer out;
|
||||
|
||||
private char highSurrogate;
|
||||
|
||||
AppendableByteArray(Charset charset) {
|
||||
this(charset, DEFAULT_INITIAL_SIZE, DEFAULT_EXPANSION_SIZE);
|
||||
}
|
||||
@@ -89,8 +91,19 @@ class AppendableByteArray implements Appendable {
|
||||
}
|
||||
|
||||
private AppendableByteArray append(CharBuffer in) throws IOException {
|
||||
CoderResult result = this.encoder.encode(in, this.out, false);
|
||||
if (this.highSurrogate != 0) {
|
||||
CharBuffer pending = CharBuffer.allocate(in.remaining() + 1);
|
||||
pending.put(this.highSurrogate).put(in).flip();
|
||||
this.highSurrogate = 0;
|
||||
in = pending;
|
||||
}
|
||||
return append(in, false);
|
||||
}
|
||||
|
||||
private AppendableByteArray append(CharBuffer in, boolean endOfInput) throws IOException {
|
||||
CoderResult result = this.encoder.encode(in, this.out, endOfInput);
|
||||
if (result.isUnderflow()) {
|
||||
this.highSurrogate = (in.hasRemaining()) ? in.get() : 0;
|
||||
return this;
|
||||
}
|
||||
if (result.isOverflow()) {
|
||||
@@ -98,13 +111,18 @@ class AppendableByteArray implements Appendable {
|
||||
this.out = ByteBuffer.allocate(out.capacity() + this.expansionSize);
|
||||
out.flip();
|
||||
this.out.put(out);
|
||||
return append(in);
|
||||
return append(in, endOfInput);
|
||||
}
|
||||
result.throwException();
|
||||
return this;
|
||||
}
|
||||
|
||||
byte[] toByteArray() {
|
||||
byte[] toByteArray() throws IOException {
|
||||
if (this.highSurrogate != 0) {
|
||||
CharBuffer in = CharBuffer.wrap(new char[] { this.highSurrogate });
|
||||
this.highSurrogate = 0;
|
||||
append(in, true);
|
||||
}
|
||||
this.out.flip();
|
||||
int limit = this.out.limit();
|
||||
int position = this.out.position();
|
||||
@@ -120,6 +138,7 @@ class AppendableByteArray implements Appendable {
|
||||
private void reset() {
|
||||
this.out.clear();
|
||||
this.encoder.reset();
|
||||
this.highSurrogate = 0;
|
||||
}
|
||||
|
||||
static byte[] toByteArray(Charset charset, ThrowingConsumer<Appendable> appendable) throws IOException {
|
||||
|
||||
+16
@@ -90,6 +90,22 @@ class AppendableByteArrayTests {
|
||||
assertThat(reused).isEqualTo("clean".getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
@Test
|
||||
void writesSurrogatePairAppendedAsIndividualChars() throws Exception {
|
||||
assertByteArray(StandardCharsets.UTF_8, (appendable) -> appendable.append('\uD83D').append('\uDE00'));
|
||||
assertByteArray(StandardCharsets.UTF_16, (appendable) -> appendable.append('\uD83D').append('\uDE00'));
|
||||
}
|
||||
|
||||
@Test
|
||||
void writesSurrogatePairSplitAcrossAppendedStrings() throws Exception {
|
||||
assertByteArray(StandardCharsets.UTF_8, (appendable) -> appendable.append("a\uD83D").append("\uDE00b"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void writesUnpairedHighSurrogate() throws Exception {
|
||||
assertByteArray(StandardCharsets.UTF_8, (appendable) -> appendable.append('\uD83D'));
|
||||
}
|
||||
|
||||
private void assertByteArray(Charset charset, ThrowingConsumer<Appendable> action) throws Exception {
|
||||
assertByteArray(4, 4, charset, action);
|
||||
}
|
||||
|
||||
@@ -66,6 +66,17 @@ class WritableJsonTests {
|
||||
assertThat(writable.toByteArray()).isEqualTo("{}".getBytes());
|
||||
}
|
||||
|
||||
@Test
|
||||
void toByteArrayWhenContentIsAppendedCharByCharWritesSupplementaryCharacters() {
|
||||
String emoji = new String(Character.toChars(0x1F600));
|
||||
WritableJson writable = (out) -> {
|
||||
for (int i = 0; i < emoji.length(); i++) {
|
||||
out.append(emoji.charAt(i));
|
||||
}
|
||||
};
|
||||
assertThat(writable.toByteArray(StandardCharsets.UTF_8)).isEqualTo(emoji.getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
@Test
|
||||
void toResourceWritesJson() throws Exception {
|
||||
File file = new File(this.temp, "out.json");
|
||||
|
||||
Reference in New Issue
Block a user