From 725aa5c6e85eb6a8cddb579a662c7eca8d58c71d Mon Sep 17 00:00:00 2001 From: Catnies Date: Tue, 24 Jun 2025 05:30:49 +0800 Subject: [PATCH 1/7] =?UTF-8?q?=E6=94=B9=E8=BF=9B=20SNBTReader#parsePrimit?= =?UTF-8?q?ive?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...{SNBTDeserializer.java => SNBTReader.java} | 82 ++++++++++++------- 1 file changed, 52 insertions(+), 30 deletions(-) rename core/src/main/java/net/momirealms/craftengine/core/util/{SNBTDeserializer.java => SNBTReader.java} (80%) diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/SNBTDeserializer.java b/core/src/main/java/net/momirealms/craftengine/core/util/SNBTReader.java similarity index 80% rename from core/src/main/java/net/momirealms/craftengine/core/util/SNBTDeserializer.java rename to core/src/main/java/net/momirealms/craftengine/core/util/SNBTReader.java index 573493ce5..eeaf05a09 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/SNBTDeserializer.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/SNBTReader.java @@ -177,47 +177,43 @@ public final class SNBTDeserializer { // 解析原生值 private Object parsePrimitive() { - skipWhitespace(); - int tokenStart = position; - // 先解析获取值的长度 + int tokenStart = position; while (position < length) { char c = sourceContent[position]; if (c <= ' ' || c == ',' || c == ']' || c == '}') break; position++; } int tokenLength = position - tokenStart; - if (tokenLength == 0) { - throw new IllegalArgumentException("Empty value at position " + tokenStart); - } + if (tokenLength == 0) throw new IllegalArgumentException("Empty value at position " + tokenStart); // 布尔值快速检查 - if (tokenLength == TRUE_LENGTH && matchesAt(tokenStart, TRUE_LITERAL)) { - return Boolean.TRUE; - } - if (tokenLength == FALSE_LENGTH && matchesAt(tokenStart, FALSE_LITERAL)) { - return Boolean.FALSE; - } + if (tokenLength == TRUE_LENGTH && matchesAt(tokenStart, TRUE_LITERAL)) return Boolean.TRUE; + if (tokenLength == FALSE_LENGTH && matchesAt(tokenStart, FALSE_LITERAL)) return Boolean.FALSE; + + // 无后缀数字处理 + if (isNumber(tokenStart, tokenStart + tokenLength) == 1) return Integer.parseInt(new String(sourceContent, tokenStart, tokenLength)); + if (isNumber(tokenStart, tokenStart + tokenLength) == 2) return Double.parseDouble(new String(sourceContent, tokenStart, tokenLength)); // 带后缀的值处理 - try { - char lastChar = sourceContent[tokenStart + tokenLength - 1]; - if (tokenLength > 1 && isTypeSuffix(lastChar)) { - return switch (lastChar) { - case BYTE_SUFFIX -> parseByte(tokenStart, tokenLength - 1); - case SHORT_SUFFIX -> parseShort(tokenStart, tokenLength - 1); - case LONG_SUFFIX -> parseLong(tokenStart, tokenLength - 1); - case FLOAT_SUFFIX -> Float.parseFloat(new String(sourceContent, tokenStart, tokenLength - 1)); - case DOUBLE_SUFFIX -> Double.parseDouble(new String(sourceContent, tokenStart, tokenLength - 1)); - case BOOLEAN_SUFFIX -> parseBoolean(new String(sourceContent, tokenStart, tokenLength - 1)); - default -> throw new IllegalArgumentException("Invalid suffixed value " + new String(sourceContent, tokenStart, tokenLength - 1)); - }; - } - // 没有后缀就默认为 double 喵 - return Double.parseDouble(new String(sourceContent, tokenStart, tokenLength)); - } catch (NumberFormatException e) { - throw new IllegalArgumentException("Invalid number value at position " + tokenStart, e); + char lastChar = sourceContent[tokenStart + tokenLength - 1]; + if (tokenLength > 1 && // 要求: 长度>1 + isTypeSuffix(lastChar) && // 要求: 有效后缀 + isNumber(tokenStart, tokenStart + tokenLength - 1) > 0 // 要求: 除后缀外是合法数字 + ) { + return switch (lastChar) { + case BYTE_SUFFIX -> parseByte(tokenStart, tokenLength - 1); + case SHORT_SUFFIX -> parseShort(tokenStart, tokenLength - 1); + case LONG_SUFFIX -> parseLong(tokenStart, tokenLength - 1); + case FLOAT_SUFFIX -> Float.parseFloat(new String(sourceContent, tokenStart, tokenLength - 1)); + case DOUBLE_SUFFIX -> Double.parseDouble(new String(sourceContent, tokenStart, tokenLength - 1)); + case BOOLEAN_SUFFIX -> parseBoolean(new String(sourceContent, tokenStart, tokenLength - 1)); + default -> new IllegalArgumentException("Parse Error with: " + new String(sourceContent, tokenStart, tokenLength - 1)); // 永远不应进入此 case. + }; } + + // 都无法匹配就默认为 String 喵~ + return new String(sourceContent, tokenStart, tokenLength); } // 工具函数: 快速检查布尔值字符串匹配, 忽略大小写. @@ -317,7 +313,6 @@ public final class SNBTDeserializer { return sourceContent[position]; } - // 跳过空格 private void skipWhitespace() { while (position < length) { @@ -331,4 +326,31 @@ public final class SNBTDeserializer { } } + // 检查是不是合法数字. + // 返回0代表不合法, 1代表整数, 2代表小数. + private int isNumber(int start, int end) { + // 跳过负号 + if (sourceContent[start] == '-') start++; + + // 除负号外第一个字符必须是数字. + char c1 = sourceContent[start]; + if (c1 < '0' || c1 > '9') return 0; + + // 检查剩余的数字, 只能有一个小数点 + boolean hasDecimal = false; + for (; start < end; start++) { + char c = sourceContent[start]; + if (c < '0' || c > '9') { + if (c == '.') { + if (hasDecimal) return 0; // bro 不能有2个小数点. + hasDecimal = true; + } else { + return 0; + } + } + } + + return hasDecimal ? 2 : 1; + } + } From 424d4c3c9aa05b89f1615de423d71b54f5065471 Mon Sep 17 00:00:00 2001 From: Catnies Date: Tue, 24 Jun 2025 05:46:09 +0800 Subject: [PATCH 2/7] =?UTF-8?q?=E6=94=B9=E8=BF=9B=20SNBTReader,=20?= =?UTF-8?q?=E6=94=AF=E6=8C=81=E5=8D=95=E5=BC=95=E5=8F=B7=E5=AD=97=E7=AC=A6?= =?UTF-8?q?=E4=B8=B2=E5=88=86=E9=9A=94=E7=AC=A6=20.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...{SNBTReader.java => SNBTDeserializer.java} | 131 +++++++++--------- 1 file changed, 66 insertions(+), 65 deletions(-) rename core/src/main/java/net/momirealms/craftengine/core/util/{SNBTReader.java => SNBTDeserializer.java} (74%) diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/SNBTReader.java b/core/src/main/java/net/momirealms/craftengine/core/util/SNBTDeserializer.java similarity index 74% rename from core/src/main/java/net/momirealms/craftengine/core/util/SNBTReader.java rename to core/src/main/java/net/momirealms/craftengine/core/util/SNBTDeserializer.java index eeaf05a09..ec34b9bbe 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/SNBTReader.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/SNBTDeserializer.java @@ -11,6 +11,8 @@ public final class SNBTDeserializer { private static final char LIST_START = '['; private static final char LIST_END = ']'; private static final char STRING_DELIMITER = '"'; + private static final char SINGLE_QUOTES = '\''; + private static final char DOUBLE_QUOTES = '"'; private static final char KEY_VALUE_SEPARATOR = ':'; private static final char ELEMENT_SEPARATOR = ','; private static final char ESCAPE_CHAR = '\\'; @@ -23,15 +25,10 @@ public final class SNBTDeserializer { private static final char DOUBLE_SUFFIX = 'd'; private static final char BOOLEAN_SUFFIX = 'B'; - // 布尔值常量 - private static final String TRUE_LITERAL = "true"; - private static final String FALSE_LITERAL = "false"; - private static final int TRUE_LENGTH = 4; - private static final int FALSE_LENGTH = 5; - + // 使用 char[] 处理获得更高的性能 private final char[] sourceContent; private final int length; - private int position = 0; + private int cursor = 0; private SNBTDeserializer(String content) { this.sourceContent = content.toCharArray(); @@ -44,10 +41,10 @@ public final class SNBTDeserializer { Object result = parser.parseValue(); parser.skipWhitespace(); - if (parser.position != parser.length) { + if (parser.cursor != parser.length) { throw new IllegalArgumentException("Extra content at end: " + - new String(parser.sourceContent, parser.position, - parser.length - parser.position)); + new String(parser.sourceContent, parser.cursor, + parser.length - parser.cursor)); } return result; } @@ -58,92 +55,94 @@ public final class SNBTDeserializer { return switch (peekCurrentChar()) { case COMPOUND_START -> parseCompound(); case LIST_START -> parseList(); - case STRING_DELIMITER -> parseString(); + case DOUBLE_QUOTES -> parseString(DOUBLE_QUOTES); + case SINGLE_QUOTES -> parseString(SINGLE_QUOTES); default -> parsePrimitive(); }; } // 解析包小肠 {} private Map parseCompound() { - position++; // 跳过 '{' + cursor++; // 跳过 '{' skipWhitespace(); Map compoundMap = new LinkedHashMap<>(16); // 避免一次扩容, 应该有一定的性能提升 - if (position < length && sourceContent[position] != COMPOUND_END) { + if (cursor < length && sourceContent[cursor] != COMPOUND_END) { do { String key = parseKey(); - if (position >= length || sourceContent[position] != KEY_VALUE_SEPARATOR) { - throw new IllegalArgumentException("Expected ':' at position " + position); + if (cursor >= length || sourceContent[cursor] != KEY_VALUE_SEPARATOR) { + throw new IllegalArgumentException("Expected ':' at position " + cursor); } - position++; // 跳过 ':' + cursor++; // 跳过 ':' Object value = parseValue(); compoundMap.put(key, value); skipWhitespace(); - } while (position < length && sourceContent[position] == ELEMENT_SEPARATOR && ++position > 0); + } while (cursor < length && sourceContent[cursor] == ELEMENT_SEPARATOR && ++cursor > 0); } - if (position >= length || sourceContent[position] != COMPOUND_END) { - throw new IllegalArgumentException("Expected '}' at position " + position); + if (cursor >= length || sourceContent[cursor] != COMPOUND_END) { + throw new IllegalArgumentException("Expected '}' at position " + cursor); } - position++; // 跳过 '}' + cursor++; // 跳过 '}' return compoundMap; } // 解析列表值 [1, 2, 3] private List parseList() { - position++; // 跳过 '[' + cursor++; // 跳过 '[' skipWhitespace(); List elementList = new ArrayList<>(); - if (position < length && sourceContent[position] != LIST_END) { + if (cursor < length && sourceContent[cursor] != LIST_END) { do { elementList.add(parseValue()); skipWhitespace(); - } while (position < length && sourceContent[position] == ELEMENT_SEPARATOR && ++position > 0); + } while (cursor < length && sourceContent[cursor] == ELEMENT_SEPARATOR && ++cursor > 0); } - if (position >= length || sourceContent[position] != LIST_END) { - throw new IllegalArgumentException("Expected ']' at position " + position); + if (cursor >= length || sourceContent[cursor] != LIST_END) { + throw new IllegalArgumentException("Expected ']' at position " + cursor); } - position++; // 跳过 ']' + cursor++; // 跳过 ']' return elementList; } // 解析字符串 - private String parseString() { - position++; // 跳过开始的引号 - int start = position; + private String parseString(char delimiter) { + cursor++; // 跳过开始的引号 + int start = cursor; // 扫一次字符串, 如果没有发现转义就直接返回, 发现了就再走转义解析. // 这样可以避免创建一次 StringBuilder. - while (position < length) { - char c = sourceContent[position]; - if (c == STRING_DELIMITER) { - String result = new String(sourceContent, start, position - start); - position++; // 跳过结束引号 + while (cursor < length) { + char c = sourceContent[cursor]; + if (c == delimiter) { + String result = new String(sourceContent, start, cursor - start); + cursor++; // 跳过结束引号 return result; // 没有转义直接返回字符串. } - // 如果发现转义字符, + // 如果发现转义字符,进行转义处理. else if (c == ESCAPE_CHAR) { - return parseStringWithEscape(start); + return parseStringWithEscape(start, delimiter); } - position++; + cursor++; } // 没有扫描到结束引号 throw new IllegalArgumentException("Unterminated string at " + start); } // 处理含转义的字符串 - private String parseStringWithEscape(int start) { - StringBuilder sb = new StringBuilder(position - start + 16); - sb.append(sourceContent, start, position - start); + private String parseStringWithEscape(int start, char delimiter) { + // 先把之前遍历没有包含转义的字符保存 + StringBuilder sb = new StringBuilder(cursor - start + 16); + sb.append(sourceContent, start, cursor - start); - while (position < length) { - char c = sourceContent[position++]; - if (c == ESCAPE_CHAR && position < length) { - sb.append(getEscapedChar(sourceContent[position++])); - } else if (c == STRING_DELIMITER) { // 字符 + while (cursor < length) { + char c = sourceContent[cursor++]; + if (c == ESCAPE_CHAR && cursor < length) { + sb.append(getEscapedChar(sourceContent[cursor++])); // 解析转义. + } else if (c == delimiter) { // 发现结束分隔字符, 返回. return sb.toString(); } else { sb.append(c); @@ -157,39 +156,40 @@ public final class SNBTDeserializer { private String parseKey() { skipWhitespace(); // 如果有双引号就委托给string解析处理. - if (position < length && sourceContent[position] == STRING_DELIMITER) { - return parseString(); + if (cursor < length) { + if (sourceContent[cursor] == STRING_DELIMITER) return parseString(STRING_DELIMITER); + if (sourceContent[cursor] == SINGLE_QUOTES) return parseString(SINGLE_QUOTES); } - int start = position; - while (position < length) { - char c = sourceContent[position]; + int start = cursor; + while (cursor < length) { + char c = sourceContent[cursor]; if (Character.isJavaIdentifierPart(c)) { - position++; + cursor++; } else { break; } } skipWhitespace(); - return new String(sourceContent, start, position - start); + return new String(sourceContent, start, cursor - start); } // 解析原生值 private Object parsePrimitive() { // 先解析获取值的长度 - int tokenStart = position; - while (position < length) { - char c = sourceContent[position]; + int tokenStart = cursor; + while (cursor < length) { + char c = sourceContent[cursor]; if (c <= ' ' || c == ',' || c == ']' || c == '}') break; - position++; + cursor++; } - int tokenLength = position - tokenStart; + int tokenLength = cursor - tokenStart; if (tokenLength == 0) throw new IllegalArgumentException("Empty value at position " + tokenStart); // 布尔值快速检查 - if (tokenLength == TRUE_LENGTH && matchesAt(tokenStart, TRUE_LITERAL)) return Boolean.TRUE; - if (tokenLength == FALSE_LENGTH && matchesAt(tokenStart, FALSE_LITERAL)) return Boolean.FALSE; + if (tokenLength == 4 && matchesAt(tokenStart, "true")) return Boolean.TRUE; + if (tokenLength == 5 && matchesAt(tokenStart, "false")) return Boolean.FALSE; // 无后缀数字处理 if (isNumber(tokenStart, tokenStart + tokenLength) == 1) return Integer.parseInt(new String(sourceContent, tokenStart, tokenLength)); @@ -295,7 +295,8 @@ public final class SNBTDeserializer { // 转义字符处理 private char getEscapedChar(char escapedChar) { return switch (escapedChar) { - case STRING_DELIMITER -> '"'; + case DOUBLE_QUOTES -> '"'; + case SINGLE_QUOTES -> '\''; case ESCAPE_CHAR -> '\\'; case COMPOUND_START -> '{'; case COMPOUND_END -> '}'; @@ -309,17 +310,17 @@ public final class SNBTDeserializer { // 获取当前字符 private char peekCurrentChar() { - if (position >= length) throw new IllegalArgumentException("Unexpected end of input at position " + position); - return sourceContent[position]; + if (cursor >= length) throw new IllegalArgumentException("Unexpected end of input at position " + cursor); + return sourceContent[cursor]; } // 跳过空格 private void skipWhitespace() { - while (position < length) { - char c = sourceContent[position]; + while (cursor < length) { + char c = sourceContent[cursor]; if (c > ' ') { break; } // 大于空格的字符都不是空白字符 if (c == ' ' || c == '\t' || c == '\n' || c == '\r') { - position++; + cursor++; } else { break; } From 113827f62162c637da2d43b7188414e8d1c798df Mon Sep 17 00:00:00 2001 From: Catnies Date: Tue, 24 Jun 2025 07:37:36 +0800 Subject: [PATCH 3/7] =?UTF-8?q?=E6=94=B9=E8=BF=9B=20SNBTReader,=20?= =?UTF-8?q?=E4=BD=BF=E5=85=B6=E7=BB=A7=E6=89=BFDefaultStringReader.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...{SNBTDeserializer.java => SNBTReader.java} | 189 +++++------------- 1 file changed, 51 insertions(+), 138 deletions(-) rename core/src/main/java/net/momirealms/craftengine/core/util/{SNBTDeserializer.java => SNBTReader.java} (55%) diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/SNBTDeserializer.java b/core/src/main/java/net/momirealms/craftengine/core/util/SNBTReader.java similarity index 55% rename from core/src/main/java/net/momirealms/craftengine/core/util/SNBTDeserializer.java rename to core/src/main/java/net/momirealms/craftengine/core/util/SNBTReader.java index ec34b9bbe..f7bc00d6e 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/SNBTDeserializer.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/SNBTReader.java @@ -5,7 +5,7 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; -public final class SNBTDeserializer { +public final class SNBTReader extends DefaultStringReader { private static final char COMPOUND_START = '{'; private static final char COMPOUND_END = '}'; private static final char LIST_START = '['; @@ -23,36 +23,29 @@ public final class SNBTDeserializer { private static final char LONG_SUFFIX = 'l'; private static final char FLOAT_SUFFIX = 'f'; private static final char DOUBLE_SUFFIX = 'd'; - private static final char BOOLEAN_SUFFIX = 'B'; - // 使用 char[] 处理获得更高的性能 - private final char[] sourceContent; private final int length; - private int cursor = 0; - - private SNBTDeserializer(String content) { - this.sourceContent = content.toCharArray(); - this.length = sourceContent.length; + private SNBTReader(String content) { + super(content); + this.length = this.string.length(); } // 入口API public static Object deserializeAsJava(String input) throws IllegalArgumentException { - SNBTDeserializer parser = new SNBTDeserializer(input); + SNBTReader parser = new SNBTReader(input); Object result = parser.parseValue(); parser.skipWhitespace(); - if (parser.cursor != parser.length) { - throw new IllegalArgumentException("Extra content at end: " + - new String(parser.sourceContent, parser.cursor, - parser.length - parser.cursor)); - } + if (parser.cursor != parser.length) + throw new IllegalArgumentException("Extra content at end: " + parser.string.substring(parser.cursor, parser.length - parser.cursor)); + return result; } // 开始解析, 步进字符. private Object parseValue() { skipWhitespace(); - return switch (peekCurrentChar()) { + return switch (peek()) { case COMPOUND_START -> parseCompound(); case LIST_START -> parseList(); case DOUBLE_QUOTES -> parseString(DOUBLE_QUOTES); @@ -68,20 +61,20 @@ public final class SNBTDeserializer { Map compoundMap = new LinkedHashMap<>(16); // 避免一次扩容, 应该有一定的性能提升 - if (cursor < length && sourceContent[cursor] != COMPOUND_END) { + if (cursor < length && peek() != COMPOUND_END) { do { String key = parseKey(); - if (cursor >= length || sourceContent[cursor] != KEY_VALUE_SEPARATOR) { + if (cursor >= length || peek() != KEY_VALUE_SEPARATOR) { throw new IllegalArgumentException("Expected ':' at position " + cursor); } cursor++; // 跳过 ':' Object value = parseValue(); compoundMap.put(key, value); skipWhitespace(); - } while (cursor < length && sourceContent[cursor] == ELEMENT_SEPARATOR && ++cursor > 0); + } while (cursor < length && peek() == ELEMENT_SEPARATOR && ++cursor > 0); } - if (cursor >= length || sourceContent[cursor] != COMPOUND_END) { + if (cursor >= length || peek() != COMPOUND_END) { throw new IllegalArgumentException("Expected '}' at position " + cursor); } cursor++; // 跳过 '}' @@ -94,14 +87,14 @@ public final class SNBTDeserializer { skipWhitespace(); List elementList = new ArrayList<>(); - if (cursor < length && sourceContent[cursor] != LIST_END) { + if (cursor < length && peek() != LIST_END) { do { elementList.add(parseValue()); skipWhitespace(); - } while (cursor < length && sourceContent[cursor] == ELEMENT_SEPARATOR && ++cursor > 0); + } while (cursor < length && peek() == ELEMENT_SEPARATOR && ++cursor > 0); } - if (cursor >= length || sourceContent[cursor] != LIST_END) { + if (cursor >= length || peek() != LIST_END) { throw new IllegalArgumentException("Expected ']' at position " + cursor); } cursor++; // 跳过 ']' @@ -116,9 +109,9 @@ public final class SNBTDeserializer { // 扫一次字符串, 如果没有发现转义就直接返回, 发现了就再走转义解析. // 这样可以避免创建一次 StringBuilder. while (cursor < length) { - char c = sourceContent[cursor]; + char c = peek(); if (c == delimiter) { - String result = new String(sourceContent, start, cursor - start); + String result = string.substring(start, cursor); cursor++; // 跳过结束引号 return result; // 没有转义直接返回字符串. } @@ -136,12 +129,12 @@ public final class SNBTDeserializer { private String parseStringWithEscape(int start, char delimiter) { // 先把之前遍历没有包含转义的字符保存 StringBuilder sb = new StringBuilder(cursor - start + 16); - sb.append(sourceContent, start, cursor - start); + sb.append(string, start, cursor); while (cursor < length) { - char c = sourceContent[cursor++]; + char c = read(); if (c == ESCAPE_CHAR && cursor < length) { - sb.append(getEscapedChar(sourceContent[cursor++])); // 解析转义. + sb.append(getEscapedChar(read())); // 解析转义. } else if (c == delimiter) { // 发现结束分隔字符, 返回. return sb.toString(); } else { @@ -157,22 +150,17 @@ public final class SNBTDeserializer { skipWhitespace(); // 如果有双引号就委托给string解析处理. if (cursor < length) { - if (sourceContent[cursor] == STRING_DELIMITER) return parseString(STRING_DELIMITER); - if (sourceContent[cursor] == SINGLE_QUOTES) return parseString(SINGLE_QUOTES); + if (peek() == STRING_DELIMITER) return parseString(STRING_DELIMITER); + if (peek() == SINGLE_QUOTES) return parseString(SINGLE_QUOTES); } int start = cursor; while (cursor < length) { - char c = sourceContent[cursor]; - if (Character.isJavaIdentifierPart(c)) { - cursor++; - } else { - break; - } + if (Character.isJavaIdentifierPart(peek())) cursor++; else break; } skipWhitespace(); - return new String(sourceContent, start, cursor - start); + return string.substring(start, cursor); } // 解析原生值 @@ -180,46 +168,51 @@ public final class SNBTDeserializer { // 先解析获取值的长度 int tokenStart = cursor; while (cursor < length) { - char c = sourceContent[cursor]; + char c = peek(); if (c <= ' ' || c == ',' || c == ']' || c == '}') break; cursor++; } int tokenLength = cursor - tokenStart; if (tokenLength == 0) throw new IllegalArgumentException("Empty value at position " + tokenStart); + String fullContent = string.substring(tokenStart, tokenStart + tokenLength); // 布尔值快速检查 - if (tokenLength == 4 && matchesAt(tokenStart, "true")) return Boolean.TRUE; - if (tokenLength == 5 && matchesAt(tokenStart, "false")) return Boolean.FALSE; + if ("1B".equals(fullContent) || (tokenLength == 4 && matchesAt(tokenStart, "true"))) return Boolean.TRUE; + if ("0B".equals(fullContent) || (tokenLength == 5 && matchesAt(tokenStart, "false"))) return Boolean.FALSE; // 无后缀数字处理 - if (isNumber(tokenStart, tokenStart + tokenLength) == 1) return Integer.parseInt(new String(sourceContent, tokenStart, tokenLength)); - if (isNumber(tokenStart, tokenStart + tokenLength) == 2) return Double.parseDouble(new String(sourceContent, tokenStart, tokenLength)); + if (isNumber(tokenStart, tokenStart + tokenLength) == 1) return Integer.parseInt(fullContent); + if (isNumber(tokenStart, tokenStart + tokenLength) == 2) return Double.parseDouble(fullContent); // 带后缀的值处理 - char lastChar = sourceContent[tokenStart + tokenLength - 1]; + char lastChar = string.charAt(tokenStart + tokenLength - 1); if (tokenLength > 1 && // 要求: 长度>1 isTypeSuffix(lastChar) && // 要求: 有效后缀 isNumber(tokenStart, tokenStart + tokenLength - 1) > 0 // 要求: 除后缀外是合法数字 ) { - return switch (lastChar) { - case BYTE_SUFFIX -> parseByte(tokenStart, tokenLength - 1); - case SHORT_SUFFIX -> parseShort(tokenStart, tokenLength - 1); - case LONG_SUFFIX -> parseLong(tokenStart, tokenLength - 1); - case FLOAT_SUFFIX -> Float.parseFloat(new String(sourceContent, tokenStart, tokenLength - 1)); - case DOUBLE_SUFFIX -> Double.parseDouble(new String(sourceContent, tokenStart, tokenLength - 1)); - case BOOLEAN_SUFFIX -> parseBoolean(new String(sourceContent, tokenStart, tokenLength - 1)); - default -> new IllegalArgumentException("Parse Error with: " + new String(sourceContent, tokenStart, tokenLength - 1)); // 永远不应进入此 case. - }; + final String content = string.substring(tokenStart, tokenStart + tokenLength - 1); + try { + return switch (lastChar) { + case BYTE_SUFFIX -> Byte.parseByte(content); + case SHORT_SUFFIX -> Short.parseShort(content); + case LONG_SUFFIX -> Long.parseLong(content); + case FLOAT_SUFFIX -> Float.parseFloat(content); + case DOUBLE_SUFFIX -> Double.parseDouble(content); + default -> new IllegalArgumentException("Parse Error with: " + content); // 永远不应进入此 case. + }; + } catch (NumberFormatException e) { + return fullContent; // 如果有神人写了 128b 一类的超范围值, 就当做字符串返回好了. + } } // 都无法匹配就默认为 String 喵~ - return new String(sourceContent, tokenStart, tokenLength); + return fullContent; } // 工具函数: 快速检查布尔值字符串匹配, 忽略大小写. private boolean matchesAt(int start, String target) { for (int i = 0; i < target.length(); i++) { - char c1 = sourceContent[start + i]; + char c1 = string.charAt(start + i); char c2 = target.charAt(i); if (c1 != c2 && c1 != (c2 ^ 32)) return false; // 忽略大小写比较 } @@ -228,68 +221,7 @@ public final class SNBTDeserializer { // 工具函数: 合法后缀检查 private boolean isTypeSuffix(char c) { - return c == BYTE_SUFFIX || c == SHORT_SUFFIX || c == LONG_SUFFIX || - c == FLOAT_SUFFIX || c == DOUBLE_SUFFIX || c == BOOLEAN_SUFFIX; - } - - - // 手动解析值 - private byte parseByte(int start, int length) { - int value = parseInteger(start, length); - if (value < Byte.MIN_VALUE || value > Byte.MAX_VALUE) - throw new IllegalArgumentException("Byte value out of range"); - return (byte) value; - } - - private short parseShort(int start, int length) { - int value = parseInteger(start, length); - if (value < Short.MIN_VALUE || value > Short.MAX_VALUE) - throw new NumberFormatException("Short value out of range"); - return (short) value; - } - - private Boolean parseBoolean(String content) { - if ("1".equals(content)) return Boolean.TRUE; - if ("0".equals(content)) return Boolean.FALSE; - throw new NumberFormatException("Invalid boolean value"); - } - - private int parseInteger(int start, int length) { - int result = 0; - boolean negative = false; - int i = 0; - - if (sourceContent[start] == '-') { - negative = true; - i = 1; - } - - for (; i < length; i++) { - char c = sourceContent[start + i]; - if (c < '0' || c > '9') throw new NumberFormatException("Invalid integer"); - result = result * 10 + (c - '0'); - } - - return negative ? -result : result; - } - - private long parseLong(int start, int length) { - long result = 0; - boolean negative = false; - int i = 0; - - if (sourceContent[start] == '-') { - negative = true; - i = 1; - } - - for (; i < length; i++) { - char c = sourceContent[start + i]; - if (c < '0' || c > '9') throw new NumberFormatException("Invalid long"); - result = result * 10 + (c - '0'); - } - - return negative ? -result : result; + return c == BYTE_SUFFIX || c == SHORT_SUFFIX || c == LONG_SUFFIX || c == FLOAT_SUFFIX || c == DOUBLE_SUFFIX; } // 转义字符处理 @@ -308,39 +240,20 @@ public final class SNBTDeserializer { }; } - // 获取当前字符 - private char peekCurrentChar() { - if (cursor >= length) throw new IllegalArgumentException("Unexpected end of input at position " + cursor); - return sourceContent[cursor]; - } - - // 跳过空格 - private void skipWhitespace() { - while (cursor < length) { - char c = sourceContent[cursor]; - if (c > ' ') { break; } // 大于空格的字符都不是空白字符 - if (c == ' ' || c == '\t' || c == '\n' || c == '\r') { - cursor++; - } else { - break; - } - } - } - // 检查是不是合法数字. // 返回0代表不合法, 1代表整数, 2代表小数. private int isNumber(int start, int end) { // 跳过负号 - if (sourceContent[start] == '-') start++; + if (string.charAt(start) == '-') start++; // 除负号外第一个字符必须是数字. - char c1 = sourceContent[start]; + char c1 = string.charAt(start); if (c1 < '0' || c1 > '9') return 0; // 检查剩余的数字, 只能有一个小数点 boolean hasDecimal = false; for (; start < end; start++) { - char c = sourceContent[start]; + char c = string.charAt(start); if (c < '0' || c > '9') { if (c == '.') { if (hasDecimal) return 0; // bro 不能有2个小数点. From 17129163706c2370115737cc44a39d000c36db41 Mon Sep 17 00:00:00 2001 From: Catnies Date: Tue, 24 Jun 2025 18:39:10 +0800 Subject: [PATCH 4/7] =?UTF-8?q?=E6=94=B9=E8=BF=9B=20SNBTReader,=20?= =?UTF-8?q?=E5=BF=BD=E7=95=A5=20key=20=E5=90=8E=E9=9D=A2=E7=9A=84=E7=A9=BA?= =?UTF-8?q?=E6=A0=BC.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../craftengine/core/util/SNBTReader.java | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/SNBTReader.java b/core/src/main/java/net/momirealms/craftengine/core/util/SNBTReader.java index f7bc00d6e..17c0086b7 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/SNBTReader.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/SNBTReader.java @@ -59,7 +59,7 @@ public final class SNBTReader extends DefaultStringReader { cursor++; // 跳过 '{' skipWhitespace(); - Map compoundMap = new LinkedHashMap<>(16); // 避免一次扩容, 应该有一定的性能提升 + Map compoundMap = new LinkedHashMap<>(); if (cursor < length && peek() != COMPOUND_END) { do { @@ -156,11 +156,14 @@ public final class SNBTReader extends DefaultStringReader { int start = cursor; while (cursor < length) { - if (Character.isJavaIdentifierPart(peek())) cursor++; else break; + char c = peek(); + if (c == ' ') break; // 忽略 key 后面的空格, { a :1} 应当解析成 {a:1} + if (Character.isJavaIdentifierPart(c)) cursor++; else break; } - skipWhitespace(); - return string.substring(start, cursor); + String key = string.substring(start, cursor); + skipWhitespace(); // 跳过 key 后面的空格. + return key; } // 解析原生值 @@ -176,7 +179,7 @@ public final class SNBTReader extends DefaultStringReader { if (tokenLength == 0) throw new IllegalArgumentException("Empty value at position " + tokenStart); String fullContent = string.substring(tokenStart, tokenStart + tokenLength); - // 布尔值快速检查 + // 布尔值检查 if ("1B".equals(fullContent) || (tokenLength == 4 && matchesAt(tokenStart, "true"))) return Boolean.TRUE; if ("0B".equals(fullContent) || (tokenLength == 5 && matchesAt(tokenStart, "false"))) return Boolean.FALSE; @@ -266,5 +269,4 @@ public final class SNBTReader extends DefaultStringReader { return hasDecimal ? 2 : 1; } - } From 4979d9473b7961fce360c22573ff9bb60617ab7d Mon Sep 17 00:00:00 2001 From: Catnies Date: Wed, 25 Jun 2025 01:32:59 +0800 Subject: [PATCH 5/7] =?UTF-8?q?=E6=94=B9=E8=BF=9B=20SNBTReader,=20?= =?UTF-8?q?=E6=94=AF=E6=8C=81=E6=97=A0=E5=BC=95=E5=8F=B7=E5=8C=85=E8=A3=B9?= =?UTF-8?q?=E4=B8=94=E5=B8=A6=E7=A9=BA=E6=A0=BC=E7=9A=84=E5=AD=97=E7=AC=A6?= =?UTF-8?q?=E4=B8=B2.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/net/momirealms/craftengine/core/util/SNBTReader.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/SNBTReader.java b/core/src/main/java/net/momirealms/craftengine/core/util/SNBTReader.java index 17c0086b7..d6a333645 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/SNBTReader.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/SNBTReader.java @@ -172,7 +172,7 @@ public final class SNBTReader extends DefaultStringReader { int tokenStart = cursor; while (cursor < length) { char c = peek(); - if (c <= ' ' || c == ',' || c == ']' || c == '}') break; + if (c == ',' || c == ']' || c == '}') break; cursor++; } int tokenLength = cursor - tokenStart; From 4e9903b63a3c24ccce03f367f9a6d19a7a894265 Mon Sep 17 00:00:00 2001 From: Catnies Date: Wed, 25 Jun 2025 01:48:15 +0800 Subject: [PATCH 6/7] =?UTF-8?q?=E6=94=B9=E8=BF=9B=20SNBTReader,=20?= =?UTF-8?q?=E5=8E=BB=E9=99=A4=E8=A7=A3=E6=9E=90=E7=9A=84=E6=97=A0=E5=BC=95?= =?UTF-8?q?=E5=8F=B7=E5=AD=97=E7=AC=A6=E4=B8=B2=E6=9C=AB=E5=B0=BE=E7=9A=84?= =?UTF-8?q?=E7=A9=BA=E6=A0=BC.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../net/momirealms/craftengine/core/util/SNBTReader.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/SNBTReader.java b/core/src/main/java/net/momirealms/craftengine/core/util/SNBTReader.java index d6a333645..c4904715b 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/SNBTReader.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/SNBTReader.java @@ -170,12 +170,18 @@ public final class SNBTReader extends DefaultStringReader { private Object parsePrimitive() { // 先解析获取值的长度 int tokenStart = cursor; + int lastWhitespace = 0; // 记录值末尾的空格数量,{a:炒鸡 大保健} 和 {a: 炒鸡 大保健 } 都应解析成 "炒鸡 大保健". while (cursor < length) { char c = peek(); if (c == ',' || c == ']' || c == '}') break; cursor++; + if (c == ' ') { + lastWhitespace++; // 遇到空格先增加值, 代表值尾部空格数量. + continue; + } + lastWhitespace = 0; // 遇到正常字符时清空记录的尾部空格数. } - int tokenLength = cursor - tokenStart; + int tokenLength = cursor - tokenStart - lastWhitespace; // 计算值长度需要再减去尾部空格. if (tokenLength == 0) throw new IllegalArgumentException("Empty value at position " + tokenStart); String fullContent = string.substring(tokenStart, tokenStart + tokenLength); From 8c61451201bdc977e6601e08a8641514c5d0cd26 Mon Sep 17 00:00:00 2001 From: Catnies Date: Wed, 25 Jun 2025 02:01:12 +0800 Subject: [PATCH 7/7] =?UTF-8?q?=E6=94=B9=E8=BF=9B=20SNBTReader#parsePrimit?= =?UTF-8?q?ive,=20=E9=80=82=E5=BD=93=E6=97=B6=E6=9C=BA=E5=8F=AF=E6=8F=90?= =?UTF-8?q?=E5=89=8D=E8=BF=94=E5=9B=9E.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../net/momirealms/craftengine/core/util/SNBTReader.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/SNBTReader.java b/core/src/main/java/net/momirealms/craftengine/core/util/SNBTReader.java index c4904715b..e90ff7d67 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/SNBTReader.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/SNBTReader.java @@ -171,6 +171,7 @@ public final class SNBTReader extends DefaultStringReader { // 先解析获取值的长度 int tokenStart = cursor; int lastWhitespace = 0; // 记录值末尾的空格数量,{a:炒鸡 大保健} 和 {a: 炒鸡 大保健 } 都应解析成 "炒鸡 大保健". + boolean contentHasWhitespace = false; // 记录值中有没有空格. while (cursor < length) { char c = peek(); if (c == ',' || c == ']' || c == '}') break; @@ -179,11 +180,15 @@ public final class SNBTReader extends DefaultStringReader { lastWhitespace++; // 遇到空格先增加值, 代表值尾部空格数量. continue; } - lastWhitespace = 0; // 遇到正常字符时清空记录的尾部空格数. + if (lastWhitespace > 0) { + lastWhitespace = 0; // 遇到正常字符时清空记录的尾部空格数. + contentHasWhitespace = true; + } } int tokenLength = cursor - tokenStart - lastWhitespace; // 计算值长度需要再减去尾部空格. if (tokenLength == 0) throw new IllegalArgumentException("Empty value at position " + tokenStart); String fullContent = string.substring(tokenStart, tokenStart + tokenLength); + if (contentHasWhitespace) return fullContent; // 如果值的中间有空格, 一定是字符串, 可直接返回. // 布尔值检查 if ("1B".equals(fullContent) || (tokenLength == 4 && matchesAt(tokenStart, "true"))) return Boolean.TRUE;