Improve hex and binary literal parses (#12678)

* Improve hex and binary literal parses

* Moved the specialized unsigned long parsing routine from Strings directly to LAssembler
This commit is contained in:
Cardillan
2026-09-18 16:46:33 +02:00
committed by GitHub
parent eff677e112
commit 6500b5b6de

View File

@@ -11,9 +11,6 @@ import mindustry.logic.LExecutor.*;
public class LAssembler{
public static ObjectMap<String, Func<String[], LStatement>> customParsers = new ObjectMap<>();
private static final long invalidNumNegative = Long.MIN_VALUE;
private static final long invalidNumPositive = Long.MAX_VALUE;
public boolean privileged;
/** Maps names to variable. */
public OrderedMap<String, LVar> vars = new OrderedMap<>();
@@ -110,23 +107,48 @@ public class LAssembler{
}
double parseDouble(String symbol){
//fail fast for obvious non-numbers
if(symbol.isEmpty() || !isNumStart(symbol.charAt(0))) return Double.NaN;
//parse hex/binary syntax
if(symbol.startsWith("0b")) return parseLong(false, symbol, 2, 2, symbol.length());
if(symbol.startsWith("+0b")) return parseLong(false, symbol, 2, 3, symbol.length());
if(symbol.startsWith("-0b")) return parseLong(true,symbol, 2, 3, symbol.length());
if(symbol.startsWith("0x")) return parseLong(false,symbol, 16, 2, symbol.length());
if(symbol.startsWith("+0x")) return parseLong(false,symbol, 16, 3, symbol.length());
if(symbol.startsWith("-0x")) return parseLong(true,symbol, 16, 3, symbol.length());
if(symbol.startsWith("0b")) return parseHexOrBin(false, symbol, true, 2);
if(symbol.startsWith("+0b")) return parseHexOrBin(false, symbol, true, 3);
if(symbol.startsWith("-0b")) return parseHexOrBin(true, symbol, true, 3);
if(symbol.startsWith("0x")) return parseHexOrBin(false, symbol, false, 2);
if(symbol.startsWith("+0x")) return parseHexOrBin(false, symbol, false, 3);
if(symbol.startsWith("-0x")) return parseHexOrBin(true, symbol, false, 3);
if(symbol.startsWith("%[") && symbol.endsWith("]") && symbol.length() > 3) return parseNamedColor(symbol);
if(symbol.startsWith("%") && (symbol.length() == 7 || symbol.length() == 9)) return parseColor(symbol);
return Strings.parseDouble(symbol, Double.NaN);
}
double parseLong(boolean negative, String s, int radix, int start, int end) {
long usedInvalidNum = negative ? invalidNumPositive : invalidNumNegative;
long l = Strings.parseLong(s, radix, start, end, usedInvalidNum);
return l == usedInvalidNum ? Double.NaN : negative ? -l : l;
boolean isNumStart(char c){
//note that 'e10' isn't a valid number; '%ffffff' is. Hex numbers start with '0x'.
return c >= '0' && c <= '9' || c == '.' || c == '-' || c == '+' || c == '%';
}
//parses *unsigned* hex or bin number, including negative ones (0xffffffffffffffff as -1)
//detects overflow by input length and uses bit manipulation to avoid signed arithmetics
double parseHexOrBin(boolean negative, String s, boolean binary, int offset){
int end = s.length();
if(offset >= end) return Double.NaN;
int pos = offset;
while(pos < end && s.charAt(pos) == '0') pos ++; //skip leading zeros to avoid incorrect overflow detection
int shift = binary ? 1 : 4;
if(end - pos > 64 / shift) return Double.NaN;
long acc = 0;
int radix = 1 << shift;
while(pos < end){
int digit = Character.digit(s.charAt(pos), radix);
if(digit < 0) return Double.NaN;
acc = acc << shift | digit;
pos ++;
}
return negative ? -acc : acc;
}
double parseColor(String symbol){
@@ -178,4 +200,4 @@ public class LAssembler{
return vars.get(name);
}
}
}