diff --git a/api/src/main/java/com/viaversion/viaversion/api/minecraft/nbt/BinaryTagIO.java b/api/src/main/java/com/viaversion/viaversion/api/minecraft/nbt/BinaryTagIO.java index 0b0b1c06e..d7a755cb3 100644 --- a/api/src/main/java/com/viaversion/viaversion/api/minecraft/nbt/BinaryTagIO.java +++ b/api/src/main/java/com/viaversion/viaversion/api/minecraft/nbt/BinaryTagIO.java @@ -39,8 +39,12 @@ import java.nio.file.Path; import java.util.zip.GZIPInputStream; import java.util.zip.GZIPOutputStream; +// Specific Via changes: +// - Use OpenNBT tags +// - Added readString/writeString methods from TagStringIO +// - Has not been updated for the sake of keeping the class simple /** - * See https://github.com/KyoriPowered/adventure. + * Serialization operations for binary tags. */ public final class BinaryTagIO { private BinaryTagIO() { diff --git a/api/src/main/java/com/viaversion/viaversion/api/minecraft/nbt/CharBuffer.java b/api/src/main/java/com/viaversion/viaversion/api/minecraft/nbt/CharBuffer.java index 2d4ad1c9e..83bf69a07 100644 --- a/api/src/main/java/com/viaversion/viaversion/api/minecraft/nbt/CharBuffer.java +++ b/api/src/main/java/com/viaversion/viaversion/api/minecraft/nbt/CharBuffer.java @@ -1,7 +1,7 @@ /* * This file is part of adventure, licensed under the MIT License. * - * Copyright (c) 2017-2020 KyoriPowered + * Copyright (c) 2017-2021 KyoriPowered * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -23,7 +23,10 @@ */ package com.viaversion.viaversion.api.minecraft.nbt; -/* package */ final class CharBuffer { +/** + * A character buffer designed to be inspected by a parser. + */ +final class CharBuffer { private final CharSequence sequence; private int index; @@ -32,7 +35,7 @@ package com.viaversion.viaversion.api.minecraft.nbt; } /** - * Get the character at the current position + * Get the character at the current position. * * @return The current character */ @@ -45,7 +48,7 @@ package com.viaversion.viaversion.api.minecraft.nbt; } /** - * Get the current character and advance + * Get the current character and advance. * * @return current character */ @@ -62,6 +65,10 @@ package com.viaversion.viaversion.api.minecraft.nbt; return this.index < this.sequence.length(); } + public boolean hasMore(final int offset) { + return this.index + offset < this.sequence.length(); + } + /** * Search for the provided token, and advance the reader index past the {@code until} character. * @@ -109,6 +116,23 @@ package com.viaversion.viaversion.api.minecraft.nbt; return this; } + /** + * If the next non-whitespace character is {@code token}, advance past it. + * + *
This method always consumes whitespace.
+ * + * @param token next non-whitespace character to query + * @return if the next non-whitespace character is {@code token} + */ + public boolean takeIf(final char token) { + this.skipWhitespace(); + if (this.hasMore() && this.peek() == token) { + this.advance(); + return true; + } + return false; + } + public CharBuffer skipWhitespace() { while (this.hasMore() && Character.isWhitespace(this.peek())) this.advance(); return this; diff --git a/api/src/main/java/com/viaversion/viaversion/api/minecraft/nbt/StringTagParseException.java b/api/src/main/java/com/viaversion/viaversion/api/minecraft/nbt/StringTagParseException.java index 57e6d633c..198086342 100644 --- a/api/src/main/java/com/viaversion/viaversion/api/minecraft/nbt/StringTagParseException.java +++ b/api/src/main/java/com/viaversion/viaversion/api/minecraft/nbt/StringTagParseException.java @@ -26,9 +26,9 @@ package com.viaversion.viaversion.api.minecraft.nbt; import java.io.IOException; /** - * An exception thrown when parsing a string tag + * An exception thrown when parsing a string tag. */ -/* package */ class StringTagParseException extends IOException { +class StringTagParseException extends IOException { private static final long serialVersionUID = -3001637554903912905L; private final CharSequence buffer; private final int position; diff --git a/api/src/main/java/com/viaversion/viaversion/api/minecraft/nbt/TagStringReader.java b/api/src/main/java/com/viaversion/viaversion/api/minecraft/nbt/TagStringReader.java index 6a17af19e..baef70a00 100644 --- a/api/src/main/java/com/viaversion/viaversion/api/minecraft/nbt/TagStringReader.java +++ b/api/src/main/java/com/viaversion/viaversion/api/minecraft/nbt/TagStringReader.java @@ -1,7 +1,7 @@ /* * This file is part of adventure, licensed under the MIT License. * - * Copyright (c) 2017-2020 KyoriPowered + * Copyright (c) 2017-2021 KyoriPowered * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -37,33 +37,39 @@ import com.github.steveice10.opennbt.tag.builtin.NumberTag; import com.github.steveice10.opennbt.tag.builtin.ShortTag; import com.github.steveice10.opennbt.tag.builtin.StringTag; import com.github.steveice10.opennbt.tag.builtin.Tag; +import it.unimi.dsi.fastutil.ints.IntArrayList; +import it.unimi.dsi.fastutil.ints.IntList; -import java.util.ArrayList; -import java.util.List; import java.util.stream.IntStream; +import java.util.stream.LongStream; + +// Specific Via changes: +// - Use OpenNBT tags +// - Small byteArray() optimization +// - acceptLegacy = true by default +final class TagStringReader { + private static final int MAX_DEPTH = 512; + private static final byte[] EMPTY_BYTE_ARRAY = new byte[0]; + private static final int[] EMPTY_INT_ARRAY = new int[0]; + private static final long[] EMPTY_LONG_ARRAY = new long[0]; -/** - * See https://github.com/KyoriPowered/adventure. - */ -/* package */ final class TagStringReader { private final CharBuffer buffer; + private boolean acceptLegacy = true; + private int depth; - public TagStringReader(final CharBuffer buffer) { + TagStringReader(final CharBuffer buffer) { this.buffer = buffer; } public CompoundTag compound() throws StringTagParseException { this.buffer.expect(Tokens.COMPOUND_BEGIN); final CompoundTag compoundTag = new CompoundTag(); - if (this.buffer.peek() == Tokens.COMPOUND_END) { - this.buffer.take(); + if (this.buffer.takeIf(Tokens.COMPOUND_END)) { return compoundTag; } while (this.buffer.hasMore()) { - final String key = this.key(); - final Tag tag = this.tag(); - compoundTag.put(key, tag); + compoundTag.put(this.key(), this.tag()); if (this.separatorOrCompleteWith(Tokens.COMPOUND_END)) { return compoundTag; } @@ -74,13 +80,11 @@ import java.util.stream.IntStream; public ListTag list() throws StringTagParseException { final ListTag listTag = new ListTag(); this.buffer.expect(Tokens.ARRAY_BEGIN); - final boolean prefixedIndex = this.buffer.peek() == '0' && this.buffer.peek(1) == ':'; + final boolean prefixedIndex = this.acceptLegacy && this.buffer.peek() == '0' && this.buffer.peek(1) == ':'; + if (!prefixedIndex && this.buffer.takeIf(Tokens.ARRAY_END)) { + return listTag; + } while (this.buffer.hasMore()) { - if (this.buffer.peek() == Tokens.ARRAY_END) { - this.buffer.advance(); - return listTag; - } - if (prefixedIndex) { this.buffer.takeUntil(':'); } @@ -99,11 +103,12 @@ import java.util.stream.IntStream; * * @return array-typed tag */ - public Tag array(final char elementType) throws StringTagParseException { + public Tag array(char elementType) throws StringTagParseException { this.buffer.expect(Tokens.ARRAY_BEGIN) .expect(elementType) .expect(Tokens.ARRAY_SIGNATURE_SEPARATOR); + elementType = Character.toLowerCase(elementType); if (elementType == Tokens.TYPE_BYTE) { return new ByteArrayTag(this.byteArray()); } else if (elementType == Tokens.TYPE_INT) { @@ -116,11 +121,15 @@ import java.util.stream.IntStream; } private byte[] byteArray() throws StringTagParseException { - final ListDoes not detect quoted strings, so
+ *Does not detect quoted strings, so those should have been parsed already.
* * @return a parsed tag */ @@ -227,7 +262,7 @@ import java.util.stream.IntStream; if (builder.length() != 0) { Tag result = null; try { - switch (Character.toUpperCase(current)) { // try to read and return as a number + switch (Character.toLowerCase(current)) { // try to read and return as a number // case Tokens.TYPE_INTEGER: // handled below, ints are ~special~ case Tokens.TYPE_BYTE: result = new ByteTag(Byte.parseByte(builder.toString())); @@ -269,29 +304,33 @@ import java.util.stream.IntStream; try { return new IntTag(Integer.parseInt(built)); } catch (final NumberFormatException ex) { - // ignore + try { + return new DoubleTag(Double.parseDouble(built)); + } catch (final NumberFormatException ex2) { + // ignore + } } } + + if (built.equalsIgnoreCase(Tokens.LITERAL_TRUE)) { + return new ByteTag((byte) 1); + } else if (built.equalsIgnoreCase(Tokens.LITERAL_FALSE)) { + return new ByteTag((byte) 0); + } return new StringTag(built); } private boolean separatorOrCompleteWith(final char endCharacter) throws StringTagParseException { - if (this.buffer.skipWhitespace().peek() == endCharacter) { - this.buffer.take(); + if (this.buffer.takeIf(endCharacter)) { return true; } this.buffer.expect(Tokens.VALUE_SEPARATOR); - if (this.buffer.skipWhitespace().peek() == endCharacter) { - this.buffer.take(); - return true; - } return false; } - /** - * Remove simple escape sequences from a string + * Remove simple escape sequences from a string. * * @param withEscapes input string with escapes * @return string with escapes processed @@ -310,4 +349,8 @@ import java.util.stream.IntStream; output.append(withEscapes.substring(lastEscape)); return output.toString(); } + + public void legacy(final boolean acceptLegacy) { + this.acceptLegacy = acceptLegacy; + } } diff --git a/api/src/main/java/com/viaversion/viaversion/api/minecraft/nbt/TagStringWriter.java b/api/src/main/java/com/viaversion/viaversion/api/minecraft/nbt/TagStringWriter.java index 32b67ae13..57706fc78 100644 --- a/api/src/main/java/com/viaversion/viaversion/api/minecraft/nbt/TagStringWriter.java +++ b/api/src/main/java/com/viaversion/viaversion/api/minecraft/nbt/TagStringWriter.java @@ -33,6 +33,7 @@ import com.github.steveice10.opennbt.tag.builtin.IntTag; import com.github.steveice10.opennbt.tag.builtin.ListTag; import com.github.steveice10.opennbt.tag.builtin.LongArrayTag; import com.github.steveice10.opennbt.tag.builtin.LongTag; +import com.github.steveice10.opennbt.tag.builtin.NumberTag; import com.github.steveice10.opennbt.tag.builtin.ShortTag; import com.github.steveice10.opennbt.tag.builtin.StringTag; import com.github.steveice10.opennbt.tag.builtin.Tag; @@ -41,12 +42,16 @@ import java.io.IOException; import java.io.Writer; import java.util.Map; +// Specific Via changes: +// - Use OpenNBT tags +// - Has not been updated to support pretty printing and legacy writing since that is not needed /** - * See https://github.com/KyoriPowered/adventure. + * An emitter for the SNBT format. + * + *Details on the format are described in the package documentation.
*/ -/* package */ final class TagStringWriter implements AutoCloseable { +final class TagStringWriter implements AutoCloseable { private final Appendable out; - private final String indent = " "; private int level; /** * Whether a {@link Tokens#VALUE_SEPARATOR} needs to be printed before the beginning of the next object. @@ -73,17 +78,17 @@ import java.util.Map; } else if (tag instanceof StringTag) { return this.value(((StringTag) tag).getValue(), Tokens.EOF); } else if (tag instanceof ByteTag) { - return this.value(Byte.toString(((ByteTag) tag).asByte()), Tokens.TYPE_BYTE); + return this.value(Byte.toString(((NumberTag) tag).asByte()), Tokens.TYPE_BYTE); } else if (tag instanceof ShortTag) { - return this.value(Short.toString(((ShortTag) tag).asShort()), Tokens.TYPE_SHORT); + return this.value(Short.toString(((NumberTag) tag).asShort()), Tokens.TYPE_SHORT); } else if (tag instanceof IntTag) { - return this.value(Integer.toString(((IntTag) tag).asInt()), Tokens.TYPE_INT); + return this.value(Integer.toString(((NumberTag) tag).asInt()), Tokens.TYPE_INT); } else if (tag instanceof LongTag) { - return this.value(Long.toString(((LongTag) tag).asLong()), Tokens.TYPE_LONG); + return this.value(Long.toString(((NumberTag) tag).asLong()), Character.toUpperCase(Tokens.TYPE_LONG)); // special case } else if (tag instanceof FloatTag) { - return this.value(Float.toString(((FloatTag) tag).asFloat()), Tokens.TYPE_FLOAT); + return this.value(Float.toString(((NumberTag) tag).asFloat()), Tokens.TYPE_FLOAT); } else if (tag instanceof DoubleTag) { - return this.value(Double.toString(((DoubleTag) tag).asDouble()), Tokens.TYPE_DOUBLE); + return this.value(Double.toString(((NumberTag) tag).asDouble()), Tokens.TYPE_DOUBLE); } else { throw new IOException("Unknown tag type: " + tag.getClass().getSimpleName()); // unknown! @@ -92,7 +97,7 @@ import java.util.Map; private TagStringWriter writeCompound(final CompoundTag tag) throws IOException { this.beginCompound(); - for (Map.EntryAn identifier character must match the expression {@code [a-zA-Z0-9_+.-]}
* @@ -73,8 +74,8 @@ package com.viaversion.viaversion.api.minecraft.nbt; /** * Return whether a character could be at some position in a number. - *- * A string passing this check does not necessarily mean it is syntactically valid + * + *
A string passing this check does not necessarily mean it is syntactically valid.
* * @param c character to check * @return if possibly part of a number