diff --git a/nbt/src/main/java/net/kyori/adventure/nbt/TagStringReader.java b/nbt/src/main/java/net/kyori/adventure/nbt/TagStringReader.java index aabbdb0e5..5ae45fa04 100644 --- a/nbt/src/main/java/net/kyori/adventure/nbt/TagStringReader.java +++ b/nbt/src/main/java/net/kyori/adventure/nbt/TagStringReader.java @@ -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) { diff --git a/nbt/src/test/java/net/kyori/adventure/nbt/StringIOTest.java b/nbt/src/test/java/net/kyori/adventure/nbt/StringIOTest.java index bb5c5822b..eb643881e 100644 --- a/nbt/src/test/java/net/kyori/adventure/nbt/StringIOTest.java +++ b/nbt/src/test/java/net/kyori/adventure/nbt/StringIOTest.java @@ -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