Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down Expand Up @@ -89,22 +91,38 @@ private AppendableByteArray append(char[] chars) throws IOException {
}

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()) {
ByteBuffer out = this.out;
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();
Expand All @@ -120,6 +138,7 @@ byte[] toByteArray() {
private void reset() {
this.out.clear();
this.encoder.reset();
this.highSurrogate = 0;
}

static byte[] toByteArray(Charset charset, ThrowingConsumer<Appendable> appendable) throws IOException {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,22 @@ void toByteArrayWhenPreviousUseWasAbandonedReturnsCleanInstance() throws IOExcep
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);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,17 @@ void toByteArrayReturnsByteArray() {
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");
Expand Down