From 064c9dfefd11db8d743f61ab3a4003dca63f3f7b Mon Sep 17 00:00:00 2001 From: Techcable Date: Mon, 14 Mar 2016 15:40:44 -0700 Subject: [PATCH] Optimize uuid conversions Optimizes converting to and from mojang format. Manually decode uuids to and from hex. diff --git a/api/src/main/java/io/github/waterfallmc/waterfall/utils/Hex.java b/api/src/main/java/io/github/waterfallmc/waterfall/utils/Hex.java new file mode 100644 index 00000000..4a169ebc --- /dev/null +++ b/api/src/main/java/io/github/waterfallmc/waterfall/utils/Hex.java @@ -0,0 +1,113 @@ +package io.github.waterfallmc.waterfall.utils; + +import java.util.Arrays; +import java.util.Objects; + +public class Hex { + + public static byte[] decode(CharSequence chars) { + byte[] bytes = new byte[chars.length() >> 1]; + decode(chars, 0, bytes, 0, bytes.length); + return bytes; + } + + public static void decode(char[] chars, int charOffset, byte[] dest, int offset, int length) { + decode(new CharSequence() { + @Override + public int length() { + return chars.length; + } + + @Override + public char charAt(int index) { + return chars[index]; + } + + @Override + public CharSequence subSequence(int start, int end) { + return toString().substring(start, end); + } + + @Override + public String toString() { + return new String(chars, charOffset, chars.length); + } + }); + } + + public static void decode(CharSequence chars, int charOffset, byte[] dest, int offset, int length) { + Objects.requireNonNull(chars, "Null chars"); + Objects.requireNonNull(dest, "Null destination"); + final int numChars = chars.length(); + if ((numChars & 0x01) != 0) { + throw new IllegalArgumentException("Odd number of characters: " + numChars); + } else if (length < (numChars - charOffset) >> 1) { + throw new IllegalArgumentException("Too many bytes to fill with " + numChars + " characters: " + length); + } else if (offset < 0 || charOffset < 0 || length < 0 || length * 2 > numChars - charOffset || length > dest.length - offset) { + throw new IndexOutOfBoundsException(); + } + for (int i = 0, charIndex = charOffset; i < length; i++) { + char first = chars.charAt(charIndex++); + char second = chars.charAt(charIndex++); + dest[i + offset] = (byte) ((toDigit(first) << 4) | (toDigit(second))); + } + } + + public static String encodeString(byte[] bytes) { + return new String(encode(bytes)); + } + + public static char[] encode(byte[] bytes) { + char[] chars = new char[bytes.length << 1]; + encode(chars, 0, bytes, 0, bytes.length); + return chars; + } + + public static void encode(char[] chars, int charOffset, byte[] source, int offset, int length) { + Objects.requireNonNull(chars, "Null chars"); + Objects.requireNonNull(source, "Null bytes"); + if (offset < 0 || charOffset < 0 || length < 0 || length * 2 > chars.length - charOffset || length > source.length - offset) { + throw new IndexOutOfBoundsException(); + } else if (length == 0) { + return; + } + for (int i = 0, charIndex = charOffset; i < length; i++) { + byte b = source[i + offset]; + chars[charIndex++] = fromDigit((byte) ((b >> 4) & 0xF)); + chars[charIndex++] = fromDigit((byte) (b & 0xF)); + } + } + private static final char[] ENCODE_TABLE = new char[]{ + '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' + }; + private static final byte[] DECODE_TABLE = new byte[128]; + + static { + Arrays.fill(DECODE_TABLE, (byte) -1); + for (int value = 0; value < ENCODE_TABLE.length; value++) { + char c = ENCODE_TABLE[value]; + DECODE_TABLE[c] = (byte) value; + char upper; + if ((upper = Character.toUpperCase(c)) != c) { + DECODE_TABLE[upper] = (byte) value; + } + } + } + + public static byte toDigit(char c) { + byte value; + if (c < DECODE_TABLE.length) { + value = DECODE_TABLE[c]; + } else { + value = -1; + } + if (value < 0) throw new IllegalArgumentException("Invalid character " + c); + return value; + } + + private static char fromDigit(byte b) { + assert (b & 0xF) == b : "Out of range " + b; + return ENCODE_TABLE[b]; + } +} diff --git a/api/src/main/java/io/github/waterfallmc/waterfall/utils/UUIDUtils.java b/api/src/main/java/io/github/waterfallmc/waterfall/utils/UUIDUtils.java new file mode 100644 index 00000000..cc24dd35 --- /dev/null +++ b/api/src/main/java/io/github/waterfallmc/waterfall/utils/UUIDUtils.java @@ -0,0 +1,74 @@ +package io.github.waterfallmc.waterfall.utils; +import java.util.UUID; + +import com.google.common.base.Preconditions; +import com.google.common.primitives.Longs; + +public class UUIDUtils { + private UUIDUtils() {} + + public static String undash(String id) { + return new StringBuilder( 32 ).append( id, 0, 8 ).append( id, 9, 13 ).append( id, 14, 18 ).append( id, 19, 23 ).append( id, 24, 36 ).toString(); + } + + public static String toMojangString(UUID id) { + Preconditions.checkNotNull(id, "Null id"); + return Hex.encodeString(toBytes(id)); + } + + public static UUID fromString(String s) { + Preconditions.checkNotNull(s, "Null string"); + if (s.length() == 36) { // UUID.toString() uuid + s = UUIDUtils.undash(s); + } else if (s.length() != 32) { + throw new IllegalArgumentException("Invalid UUID: " + s); + } + return fromMojangString0(s); + } + + public static UUID fromMojangString(String s) { + Preconditions.checkNotNull(s, "Null string"); + if (s.length() != 32) { + throw new IllegalArgumentException("UUID not in mojang format: " + s); + } + return fromMojangString0(s); + } + + private static UUID fromMojangString0(String s) { + assert s != null : "Null string"; + assert s.length() == 32 : "invalid length: " + s; + try { + return fromBytes(Hex.decode(s)); + } catch (IllegalArgumentException e) { + throw new IllegalArgumentException("Invalid UUID: " + s); + } + } + + public static byte[] toBytes(UUID id) { + Preconditions.checkNotNull(id, "Null id"); + byte[] result = new byte[16]; + long lsb = id.getLeastSignificantBits(); + for (int i = 15; i >= 8; i--) { + result[i] = (byte) (lsb & 0xffL); + lsb >>= 8; + } + long msb = id.getMostSignificantBits(); + for (int i = 7; i >= 0; i--) { + result[i] = (byte) (msb & 0xffL); + msb >>= 8; + } + return result; + } + + public static UUID fromBytes(byte[] bytes) { + Preconditions.checkNotNull(bytes, "Null bytes"); + if (bytes.length != 16) { + throw new IllegalArgumentException("Invalid length: " + bytes.length); + } + long msb = Longs.fromBytes(bytes[0], bytes[1], bytes[2], bytes[3], + bytes[4], bytes[5], bytes[6], bytes[7]); + long lsb = Longs.fromBytes(bytes[8], bytes[9], bytes[10], bytes[11], + bytes[12], bytes[13], bytes[14], bytes[15]); + return new UUID(msb, lsb); + } +} \ No newline at end of file diff --git a/api/src/main/java/net/md_5/bungee/Util.java b/api/src/main/java/net/md_5/bungee/Util.java index 6352951c..91efc0a6 100644 --- a/api/src/main/java/net/md_5/bungee/Util.java +++ b/api/src/main/java/net/md_5/bungee/Util.java @@ -3,6 +3,7 @@ package net.md_5.bungee; import com.google.common.base.Joiner; import com.google.common.primitives.UnsignedLongs; import io.netty.channel.unix.DomainSocketAddress; +import com.google.common.primitives.Ints; import java.net.InetSocketAddress; import java.net.SocketAddress; import java.net.URI; @@ -10,6 +11,8 @@ import java.net.URISyntaxException; import java.util.Locale; import java.util.UUID; +import io.github.waterfallmc.waterfall.utils.Hex; + /** * Series of utility classes to perform various operations. */ @@ -66,7 +69,7 @@ public class Util */ public static String hex(int i) { - return String.format( "0x%02X", i ); + return Hex.encodeString(Ints.toByteArray(i)); } /** diff --git a/api/src/main/java/net/md_5/bungee/api/ServerPing.java b/api/src/main/java/net/md_5/bungee/api/ServerPing.java index e582808f..29cd91dd 100644 --- a/api/src/main/java/net/md_5/bungee/api/ServerPing.java +++ b/api/src/main/java/net/md_5/bungee/api/ServerPing.java @@ -74,7 +74,7 @@ public class ServerPing public String getId() { - return uniqueId.toString().replace( "-", "" ); + return io.github.waterfallmc.waterfall.utils.UUIDUtils.undash( uniqueId.toString() ); // Waterfall } } diff --git a/proxy/src/main/java/net/md_5/bungee/connection/InitialHandler.java b/proxy/src/main/java/net/md_5/bungee/connection/InitialHandler.java index 1d9a9ee5..616adcf1 100644 --- a/proxy/src/main/java/net/md_5/bungee/connection/InitialHandler.java +++ b/proxy/src/main/java/net/md_5/bungee/connection/InitialHandler.java @@ -806,7 +806,7 @@ public class InitialHandler extends PacketHandler implements PendingConnection @Override public String getUUID() { - return uniqueId.toString().replace( "-", "" ); + return io.github.waterfallmc.waterfall.utils.UUIDUtils.undash( uniqueId.toString() ); // Waterfall } @Override -- 2.44.0