Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix premature SNBT parsing as number #497

Merged
merged 3 commits into from
Nov 4, 2021
Merged
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
83 changes: 38 additions & 45 deletions nbt/src/main/java/net/kyori/adventure/nbt/TagStringReader.java
Original file line number Diff line number Diff line change
Expand Up @@ -238,60 +238,53 @@ public BinaryTag tag() throws StringTagParseException {
*/
private BinaryTag scalar() {
final StringBuilder builder = new StringBuilder();
boolean possiblyNumeric = true;
int noLongerNumericAt = -1;
while (this.buffer.hasMore()) {
final char current = this.buffer.peek();
if (possiblyNumeric && !Tokens.numeric(current)) {
if (builder.length() != 0) {
BinaryTag result = null;
try {
switch (Character.toLowerCase(current)) { // try to read and return as a number
case Tokens.TYPE_BYTE:
result = ByteBinaryTag.of(Byte.parseByte(builder.toString()));
break;
case Tokens.TYPE_SHORT:
result = ShortBinaryTag.of(Short.parseShort(builder.toString()));
break;
case Tokens.TYPE_INT:
result = IntBinaryTag.of(Integer.parseInt(builder.toString()));
break;
case Tokens.TYPE_LONG:
result = LongBinaryTag.of(Long.parseLong(builder.toString()));
break;
case Tokens.TYPE_FLOAT:
final float floatValue = Float.parseFloat(builder.toString());
if (!Float.isNaN(floatValue)) { // don't accept NaN
result = FloatBinaryTag.of(floatValue);
}
break;
case Tokens.TYPE_DOUBLE:
final double doubleValue = Double.parseDouble(builder.toString());
if (!Double.isNaN(doubleValue)) { // don't accept NaN
result = DoubleBinaryTag.of(doubleValue);
}
break;
}
} catch (final NumberFormatException ex) {
possiblyNumeric = false; // fallback to treating as a String
}
if (result != null) {
this.buffer.take();
return result;
}
}
}
char current = this.buffer.peek();
if (current == '\\') { // escape -- we are significantly more lenient than original format at the moment
this.buffer.advance();
builder.append(this.buffer.take());
current = this.buffer.take();
} else if (Tokens.id(current)) {
builder.append(this.buffer.take());
this.buffer.advance();
} else { // end of value
break;
}
builder.append(current);
if (noLongerNumericAt == -1 && !Tokens.numeric(current)) {
noLongerNumericAt = builder.length();
}
}
// if we run out of content without an explicit value separator, then we're either an integer or string tag -- all others have a character at the end

final int length = builder.length();
final String built = builder.toString();
if (possiblyNumeric) {
if (noLongerNumericAt == length && length > 1) {
final char last = built.charAt(length - 1);
try {
switch (Character.toLowerCase(last)) { // try to read and return as a number
case Tokens.TYPE_BYTE:
return ByteBinaryTag.of(Byte.parseByte(built.substring(0, length - 1)));
case Tokens.TYPE_SHORT:
return ShortBinaryTag.of(Short.parseShort(built.substring(0, length - 1)));
case Tokens.TYPE_INT:
return IntBinaryTag.of(Integer.parseInt(built.substring(0, length - 1)));
case Tokens.TYPE_LONG:
return LongBinaryTag.of(Long.parseLong(built.substring(0, length - 1)));
case Tokens.TYPE_FLOAT:
final float floatValue = Float.parseFloat(built.substring(0, length - 1));
if (Float.isFinite(floatValue)) { // don't accept NaN and Infinity
return FloatBinaryTag.of(floatValue);
}
break;
case Tokens.TYPE_DOUBLE:
final double doubleValue = Double.parseDouble(built.substring(0, length - 1));
if (Double.isFinite(doubleValue)) { // don't accept NaN and Infinity
return DoubleBinaryTag.of(doubleValue);
}
break;
}
} catch (final NumberFormatException ignored) {
}
} else if (noLongerNumericAt == -1) { // if we run out of content without an explicit value separator, then we're either an integer or string tag -- all others have a character at the end
try {
return IntBinaryTag.of(Integer.parseInt(built));
} catch (final NumberFormatException ex) {
Expand Down
8 changes: 8 additions & 0 deletions nbt/src/test/java/net/kyori/adventure/nbt/StringIOTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,14 @@ void testSpecialFloatingPointNumbers() throws IOException {
assertEquals(StringBinaryTag.of("NaNd"), this.stringToTag("NaNd"));
assertEquals(StringBinaryTag.of("NaNf"), this.stringToTag("NaNf"));
assertEquals(StringBinaryTag.of("Infinityd"), this.stringToTag("Infinityd"));
assertEquals(StringBinaryTag.of("Infinityf"), this.stringToTag("Infinityf"));
}

@Test
void testPrematureNumericParsing() throws IOException {
assertEquals(StringBinaryTag.of("0da"), this.stringToTag("0da"));
assertEquals(StringBinaryTag.of("00000faa"), this.stringToTag("00000faa"));
assertEquals(StringBinaryTag.of("1350diamonds_plz"), this.stringToTag("1350diamonds_plz"));
}

@Test
Expand Down